using System.Text.Json;
using BigRegister.Domain.Submissions;

namespace BigRegister.Api.Data;

/// <summary>
/// An application (aanvraag) — the system of record the dashboard reads. A wizard
/// creates one as a Concept on its first step, syncs its draft snapshot per step,
/// then submits it into the Concept → In behandeling → Goedgekeurd/Afgewezen
/// lifecycle (ADR-0002). Status is COMPUTED ON READ (see Mappers.ToStatusDto) so
/// auto-approval is purely a function of stored timestamps — no timers, no jobs.
/// </summary>
public sealed class Aanvraag
{
    public required string Id { get; init; }
    public required string Type { get; init; }   // registratie | herregistratie | intake
    public required string Owner { get; init; }
    public JsonElement? Draft { get; set; }       // opaque wizard machine snapshot (Concept only)
    public int StepIndex { get; set; }
    public int StepCount { get; set; }
    public List<string> DocumentIds { get; set; } = new();
    public string? Referentie { get; set; }       // set on submit
    public bool AutoApprovable { get; set; }       // set on submit: duo (registratie) / other types
    public string? Reden { get; set; }             // set on submit when rejected → Afgewezen
    public bool Submitted { get; set; }
    public DateTimeOffset CreatedAt { get; init; }
    public DateTimeOffset UpdatedAt { get; set; }
    public DateTimeOffset? SubmittedAt { get; set; }
}

/// <summary>
/// In-memory application store (no DB), mirrors <see cref="DocumentStore"/>.
/// ponytail: one global lock — fine for a single-process demo; swap for per-key
/// locks if it ever serves load.
/// </summary>
public static class ApplicationStore
{
    /// After this window an auto-approvable submission reports Goedgekeurd (computed on read).
    public static readonly TimeSpan ProcessingWindow = TimeSpan.FromSeconds(8);

    private static readonly Dictionary<string, Aanvraag> _apps = new();
    private static readonly object _gate = new();

    public static Aanvraag Create(string type, string owner)
    {
        var now = DateTimeOffset.UtcNow;
        var a = new Aanvraag { Id = Guid.NewGuid().ToString(), Type = type, Owner = owner, CreatedAt = now, UpdatedAt = now };
        lock (_gate) _apps[a.Id] = a;
        return a;
    }

    public static Aanvraag? Get(string id, string owner)
    {
        lock (_gate) return _apps.TryGetValue(id, out var a) && a.Owner == owner ? a : null;
    }

    public static IReadOnlyList<Aanvraag> List(string owner)
    {
        lock (_gate) return _apps.Values.Where(a => a.Owner == owner).ToList();
    }

    /// Draft sync: idempotent upsert of the wizard snapshot. Only a Concept is mutable.
    public static bool SyncDraft(string id, string owner, JsonElement draft, int stepIndex, int stepCount, IReadOnlyList<string>? documentIds)
    {
        lock (_gate)
        {
            if (!_apps.TryGetValue(id, out var a) || a.Owner != owner || a.Submitted) return false;
            a.Draft = draft.Clone();   // detach from the request's JsonDocument (disposed after the call)
            a.StepIndex = stepIndex;
            a.StepCount = stepCount;
            if (documentIds is not null) a.DocumentIds = documentIds.ToList();
            a.UpdatedAt = DateTimeOffset.UtcNow;
            return true;
        }
    }

    /// Cancel a Concept: remove it and delete its (unlinked) documents. Linked docs
    /// (belonging to a submitted aanvraag) are left untouched by DocumentStore.
    public static bool Delete(string id, string owner)
    {
        List<string> docs;
        lock (_gate)
        {
            if (!_apps.TryGetValue(id, out var a) || a.Owner != owner) return false;
            docs = a.DocumentIds.ToList();
            _apps.Remove(id);
        }
        foreach (var d in docs) DocumentStore.DeleteOwned(d, owner);
        return true;
    }

    /// Submit transition. reject != null → Afgewezen; else accepted (In behandeling,
    /// auto-advancing to Goedgekeurd after the window when autoApprovable). Returns null
    /// if the aanvraag is gone or already submitted (idempotency guard).
    public static Aanvraag? Submit(string id, string owner, string? reject, bool autoApprovable, IReadOnlyList<string>? documentIds)
    {
        lock (_gate)
        {
            if (!_apps.TryGetValue(id, out var a) || a.Owner != owner || a.Submitted) return null;
            a.Submitted = true;
            a.SubmittedAt = DateTimeOffset.UtcNow;
            a.UpdatedAt = a.SubmittedAt.Value;
            a.Referentie = SubmissionRules.NewReference();
            a.AutoApprovable = autoApprovable;
            a.Reden = reject;
            if (documentIds is not null) a.DocumentIds = documentIds.ToList();
            return a;
        }
    }
}
