namespace KArtSell.Modules.ModelOperations.FeedbackLoop;

public enum ModelFeedbackCycleState
{
    Planned,
    PredictionFrozen,
    OutcomesMaturing,
    Evaluated,
    ImprovementProposed,
    ChallengerPlanned,
    IndependentlyValidated,
    PromotionReviewPending,
    BusinessHold,
    Closed
}

public sealed record ModelFeedbackTransition(
    ModelFeedbackCycleState From,
    ModelFeedbackCycleState To,
    string ReasonCode,
    string EvidenceHash,
    string ActorId,
    DateTimeOffset OccurredAt);

public sealed class ModelFeedbackCycle
{
    private static readonly Dictionary<ModelFeedbackCycleState, HashSet<ModelFeedbackCycleState>> Allowed =
        new()
        {
            [ModelFeedbackCycleState.Planned] = new(new[] { ModelFeedbackCycleState.PredictionFrozen, ModelFeedbackCycleState.BusinessHold }),
            [ModelFeedbackCycleState.PredictionFrozen] = new(new[] { ModelFeedbackCycleState.OutcomesMaturing, ModelFeedbackCycleState.BusinessHold }),
            [ModelFeedbackCycleState.OutcomesMaturing] = new(new[] { ModelFeedbackCycleState.Evaluated, ModelFeedbackCycleState.BusinessHold }),
            [ModelFeedbackCycleState.Evaluated] = new(new[] { ModelFeedbackCycleState.ImprovementProposed, ModelFeedbackCycleState.Closed, ModelFeedbackCycleState.BusinessHold }),
            [ModelFeedbackCycleState.ImprovementProposed] = new(new[] { ModelFeedbackCycleState.ChallengerPlanned, ModelFeedbackCycleState.Closed, ModelFeedbackCycleState.BusinessHold }),
            [ModelFeedbackCycleState.ChallengerPlanned] = new(new[] { ModelFeedbackCycleState.IndependentlyValidated, ModelFeedbackCycleState.BusinessHold }),
            [ModelFeedbackCycleState.IndependentlyValidated] = new(new[] { ModelFeedbackCycleState.PromotionReviewPending, ModelFeedbackCycleState.Closed, ModelFeedbackCycleState.BusinessHold }),
            [ModelFeedbackCycleState.PromotionReviewPending] = new(new[] { ModelFeedbackCycleState.Closed, ModelFeedbackCycleState.BusinessHold }),
            [ModelFeedbackCycleState.BusinessHold] = new(new[] { ModelFeedbackCycleState.Planned, ModelFeedbackCycleState.Closed }),
            [ModelFeedbackCycleState.Closed] = new()
        };

    private readonly List<ModelFeedbackTransition> transitions = [];

    public ModelFeedbackCycle(Guid cycleId, string scopeKey, string baseModelVersion, DateTimeOffset startedAt)
    {
        if (cycleId == Guid.Empty) throw new ArgumentException("Cycle ID is required.", nameof(cycleId));
        if (string.IsNullOrWhiteSpace(scopeKey)) throw new ArgumentException("Scope key is required.", nameof(scopeKey));
        if (string.IsNullOrWhiteSpace(baseModelVersion)) throw new ArgumentException("Base model version is required.", nameof(baseModelVersion));

        CycleId = cycleId;
        ScopeKey = scopeKey;
        BaseModelVersion = baseModelVersion;
        StartedAt = startedAt;
        State = ModelFeedbackCycleState.Planned;
        Revision = 1;
        LastOccurredAt = startedAt;
    }

    public Guid CycleId { get; }
    public string ScopeKey { get; }
    public string BaseModelVersion { get; }
    public DateTimeOffset StartedAt { get; }
    public ModelFeedbackCycleState State { get; private set; }
    public int Revision { get; private set; }
    public DateTimeOffset LastOccurredAt { get; private set; }
    public DateTimeOffset? ClosedAt { get; private set; }
    public IReadOnlyList<ModelFeedbackTransition> Transitions => transitions;

    public ModelFeedbackTransition MoveTo(
        ModelFeedbackCycleState next,
        string reasonCode,
        string evidenceHash,
        string actorId,
        DateTimeOffset occurredAt)
    {
        if (!Allowed[State].Contains(next))
            throw new InvalidOperationException($"Feedback cycle transition {State} -> {next} is not allowed.");
        if (string.IsNullOrWhiteSpace(reasonCode)) throw new ArgumentException("Reason code is required.", nameof(reasonCode));
        if (string.IsNullOrWhiteSpace(evidenceHash)) throw new ArgumentException("Evidence hash is required.", nameof(evidenceHash));
        if (string.IsNullOrWhiteSpace(actorId)) throw new ArgumentException("Actor ID is required.", nameof(actorId));
        if (occurredAt < LastOccurredAt) throw new InvalidOperationException("Feedback transition time cannot move backwards.");

        var transition = new ModelFeedbackTransition(State, next, reasonCode, evidenceHash, actorId, occurredAt);
        transitions.Add(transition);
        State = next;
        Revision++;
        LastOccurredAt = occurredAt;
        if (next == ModelFeedbackCycleState.Closed) ClosedAt = occurredAt;
        return transition;
    }

    private static HashSet<ModelFeedbackCycleState> Set(params ModelFeedbackCycleState[] states)
        => new(states);
}
