using ColaFlow.Shared.Kernel.Common;

namespace ColaFlow.Modules.Mcp.Domain.ValueObjects;

/// <summary>
/// Value object representing a preview of changes proposed by an AI agent
/// Immutable - once created, cannot be modified
/// </summary>
public sealed class DiffPreview : ValueObject
{
    /// <summary>
    /// The type of operation: CREATE, UPDATE, DELETE
    /// </summary>
    public string Operation { get; private set; }

    /// <summary>
    /// The type of entity being changed (e.g., "Epic", "Story", "Task")
    /// </summary>
    public string EntityType { get; private set; }

    /// <summary>
    /// The ID of the entity being changed (null for CREATE operations)
    /// </summary>
    public Guid? EntityId { get; private set; }

    /// <summary>
    /// Human-readable key for the entity (e.g., "COLA-146")
    /// </summary>
    public string? EntityKey { get; private set; }

    /// <summary>
    /// Snapshot of the entity state before the change (null for CREATE)
    /// Stored as JSON for flexibility
    /// </summary>
    public string? BeforeData { get; private set; }

    /// <summary>
    /// Snapshot of the entity state after the change (null for DELETE)
    /// Stored as JSON for flexibility
    /// </summary>
    public string? AfterData { get; private set; }

    /// <summary>
    /// List of individual field changes (for UPDATE operations)
    /// </summary>
    public IReadOnlyList<DiffField> ChangedFields { get; private set; }

    /// <summary>
    /// Private constructor for EF Core
    /// </summary>
    private DiffPreview()
    {
        Operation = string.Empty;
        EntityType = string.Empty;
        ChangedFields = new List<DiffField>().AsReadOnly();
    }

    /// <summary>
    /// Create a new DiffPreview
    /// </summary>
    public DiffPreview(
        string operation,
        string entityType,
        Guid? entityId,
        string? entityKey,
        string? beforeData,
        string? afterData,
        IReadOnlyList<DiffField>? changedFields = null)
    {
        // Validation
        if (string.IsNullOrWhiteSpace(operation))
            throw new ArgumentException("Operation cannot be empty", nameof(operation));

        if (string.IsNullOrWhiteSpace(entityType))
            throw new ArgumentException("EntityType cannot be empty", nameof(entityType));

        // Normalize operation to uppercase
        operation = operation.ToUpperInvariant();

        // Validate operation type
        if (operation != "CREATE" && operation != "UPDATE" && operation != "DELETE")
            throw new ArgumentException(
                "Operation must be CREATE, UPDATE, or DELETE",
                nameof(operation));

        // Validate operation-specific requirements
        if (operation == "UPDATE" && entityId == null)
            throw new ArgumentException(
                "UPDATE operation requires EntityId",
                nameof(entityId));

        if (operation == "UPDATE" && (changedFields == null || changedFields.Count == 0))
            throw new ArgumentException(
                "UPDATE operation must have at least one changed field",
                nameof(changedFields));

        if (operation == "DELETE" && entityId == null)
            throw new ArgumentException(
                "DELETE operation requires EntityId",
                nameof(entityId));

        if (operation == "CREATE" && string.IsNullOrWhiteSpace(afterData))
            throw new ArgumentException(
                "CREATE operation requires AfterData",
                nameof(afterData));

        Operation = operation;
        EntityType = entityType;
        EntityId = entityId;
        EntityKey = entityKey;
        BeforeData = beforeData;
        AfterData = afterData;
        ChangedFields = changedFields ?? new List<DiffField>().AsReadOnly();
    }

    /// <summary>
    /// Factory method to create a CREATE operation diff
    /// </summary>
    public static DiffPreview ForCreate(
        string entityType,
        string afterData,
        string? entityKey = null)
    {
        return new DiffPreview(
            operation: "CREATE",
            entityType: entityType,
            entityId: null,
            entityKey: entityKey,
            beforeData: null,
            afterData: afterData,
            changedFields: null);
    }

    /// <summary>
    /// Factory method to create an UPDATE operation diff
    /// </summary>
    public static DiffPreview ForUpdate(
        string entityType,
        Guid entityId,
        string beforeData,
        string afterData,
        IReadOnlyList<DiffField> changedFields,
        string? entityKey = null)
    {
        return new DiffPreview(
            operation: "UPDATE",
            entityType: entityType,
            entityId: entityId,
            entityKey: entityKey,
            beforeData: beforeData,
            afterData: afterData,
            changedFields: changedFields);
    }

    /// <summary>
    /// Factory method to create a DELETE operation diff
    /// </summary>
    public static DiffPreview ForDelete(
        string entityType,
        Guid entityId,
        string beforeData,
        string? entityKey = null)
    {
        return new DiffPreview(
            operation: "DELETE",
            entityType: entityType,
            entityId: entityId,
            entityKey: entityKey,
            beforeData: beforeData,
            afterData: null,
            changedFields: null);
    }

    /// <summary>
    /// Value object equality - compare all atomic values
    /// </summary>
    protected override IEnumerable<object> GetAtomicValues()
    {
        yield return Operation;
        yield return EntityType;
        yield return EntityId ?? Guid.Empty;
        yield return EntityKey ?? string.Empty;
        yield return BeforeData ?? string.Empty;
        yield return AfterData ?? string.Empty;

        foreach (var field in ChangedFields)
            yield return field;
    }

    /// <summary>
    /// Check if this is a CREATE operation
    /// </summary>
    public bool IsCreate() => Operation == "CREATE";

    /// <summary>
    /// Check if this is an UPDATE operation
    /// </summary>
    public bool IsUpdate() => Operation == "UPDATE";

    /// <summary>
    /// Check if this is a DELETE operation
    /// </summary>
    public bool IsDelete() => Operation == "DELETE";

    /// <summary>
    /// Get a human-readable summary of the change
    /// </summary>
    public string GetSummary()
    {
        var identifier = EntityKey ?? EntityId?.ToString() ?? "new entity";
        return $"{Operation} {EntityType} ({identifier})";
    }

    /// <summary>
    /// Get the count of changed fields
    /// </summary>
    public int GetChangedFieldCount() => ChangedFields.Count;
}
