namespace KArtSell.Modules.ModelOperations.Domain;

/// <summary>
/// VS-02 DOMAIN: Security Master Synchronization Policy
///
/// Handles:
/// - Conflict resolution (last-write-wins)
/// - Permission rule validation
/// - Version management
/// - Idempotency keys
///
/// Pure logic, no I/O, testable, deterministic.
/// </summary>

public record SecurityRule(
    Guid RuleId,
    string ResourceName,
    string Action,
    int Version,
    DateTime EffectiveAt,
    DateTime? ExpiresAt,
    DateTime PublishedAt,
    string CorrelationId);

public record RolePermissionAssignment(
    Guid RoleId,
    Guid RuleId,
    int Version,
    DateTime AssignedAt,
    DateTime? RemovedAt);

public record SyncState(
    int LocalVersion,
    int RemoteVersion,
    List<SecurityRule> LocalRules,
    List<SecurityRule> RemoteRules,
    string IdempotencyKey,
    string CorrelationId);

public record SyncResult(
    bool IsSuccess,
    int NewVersion,
    List<SecurityRule> AppliedRules,
    List<string> Conflicts,
    string? ErrorMessage,
    string CorrelationId);

public static class SecurityMasterPolicy
{
    /// <summary>
    /// Determine sync action: accept, reject, or rollback
    ///
    /// Rules:
    /// 1. If localVersion >= remoteVersion: Already synced (idempotent)
    /// 2. If localVersion < remoteVersion: Accept all remote rules
    /// 3. Version conflict: Reject with 409
    /// 4. Last-write-wins per rule (by PublishedAt timestamp)
    /// </summary>
    public static SyncResult ResolveSyncConflict(SyncState state)
    {
        if (state.LocalVersion > state.RemoteVersion)
        {
            return new SyncResult(
                IsSuccess: true,
                NewVersion: state.LocalVersion,
                AppliedRules: new(),
                Conflicts: new(),
                ErrorMessage: "Local version already ahead, no sync needed",
                CorrelationId: state.CorrelationId);
        }

        if (state.LocalVersion == state.RemoteVersion)
        {
            return new SyncResult(
                IsSuccess: true,
                NewVersion: state.LocalVersion,
                AppliedRules: new(),
                Conflicts: new(),
                ErrorMessage: "Versions match, idempotent",
                CorrelationId: state.CorrelationId);
        }

        var conflicts = new List<string>();
        var rulesToApply = new List<SecurityRule>();

        foreach (var remoteRule in state.RemoteRules)
        {
            var localRule = state.LocalRules.FirstOrDefault(r => r.RuleId == remoteRule.RuleId);

            if (localRule == null)
            {
                rulesToApply.Add(remoteRule);
                continue;
            }

            if (localRule.PublishedAt < remoteRule.PublishedAt)
            {
                rulesToApply.Add(remoteRule);
            }
            else if (localRule.PublishedAt == remoteRule.PublishedAt && localRule.Version < remoteRule.Version)
            {
                rulesToApply.Add(remoteRule);
                conflicts.Add($"Version conflict on rule {remoteRule.RuleId}: local {localRule.Version}, remote {remoteRule.Version}");
            }
        }

        return new SyncResult(
            IsSuccess: true,
            NewVersion: state.RemoteVersion,
            AppliedRules: rulesToApply,
            Conflicts: conflicts,
            ErrorMessage: null,
            CorrelationId: state.CorrelationId);
    }

    /// <summary>
    /// Validate rule before applying
    ///
    /// Checks:
    /// - Resource name not empty
    /// - Action in {read, write, execute}
    /// - EffectiveAt <= ExpiresAt (if set)
    /// - Timestamps in UTC
    /// </summary>
    public static (bool IsValid, List<string> Errors) ValidateRule(SecurityRule rule)
    {
        var errors = new List<string>();

        if (string.IsNullOrWhiteSpace(rule.ResourceName))
            errors.Add("ResourceName cannot be empty");

        var validActions = new[] { "read", "write", "execute" };
        if (!validActions.Contains(rule.Action.ToLowerInvariant()))
            errors.Add($"Action must be one of: {string.Join(", ", validActions)}");

        if (rule.ExpiresAt.HasValue && rule.EffectiveAt > rule.ExpiresAt)
            errors.Add("EffectiveAt must be before or equal to ExpiresAt");

        if (rule.PublishedAt.Kind != DateTimeKind.Utc)
            errors.Add("PublishedAt must be UTC");

        return (errors.Count == 0, errors);
    }

    /// <summary>
    /// Check if rule is active at given time
    /// </summary>
    public static bool IsRuleActive(SecurityRule rule, DateTime asOf)
    {
        if (asOf < rule.EffectiveAt)
            return false;

        if (rule.ExpiresAt.HasValue && asOf > rule.ExpiresAt)
            return false;

        return true;
    }

    /// <summary>
    /// Create idempotency key for sync operation
    /// Format: {fromVersion}:{correlationId}
    /// </summary>
    public static string CreateIdempotencyKey(int fromVersion, string correlationId)
    {
        return $"sync-{fromVersion}-{correlationId}";
    }

    /// <summary>
    /// Detect rollback scenario: partial sync that failed mid-transaction
    ///
    /// If applied rules don't match version increment, rollback needed.
    /// </summary>
    public static bool RequiresRollback(int appliedRuleCount, int versionIncrement)
    {
        return appliedRuleCount == 0 && versionIncrement > 0;
    }
}
