namespace KArtSell.Modules.ModelOperations.Features.ApprovalWorkflow;

using KArtSell.BuildingBlocks.Time;
using KArtSell.Modules.ModelOperations.Domain.ApprovalWorkflow;

public class CreateApprovalProposalHandler
{
    private readonly ApprovalWorkflowSql _sql;
    private readonly IClock _clock;

    public CreateApprovalProposalHandler(ApprovalWorkflowSql sql, IClock clock)
    {
        _sql = sql;
        _clock = clock;
    }

    public async Task<Guid> Handle(string userEmail, string userRole, Guid modelId, DateOnly effectiveAt, string justification, Guid correlationId, CancellationToken ct = default)
    {
        if (!ApprovalWorkflowPolicy.CanCreateProposal(userEmail, userRole))
            throw new UnauthorizedAccessException("Only Maker role can create proposals");

        var now = _clock.UtcNow.UtcDateTime;
        var proposal = new ApprovalProposal
        {
            Id = Guid.NewGuid(),
            ModelId = modelId,
            Status = ApprovalStatus.Draft,
            CreatedBy = userEmail,
            CreatedAt = now,
            Justification = justification,
            EffectiveAt = effectiveAt,
            PublishedAt = now,
            Revision = 1,
            CorrelationId = correlationId
        };

        var proposalId = await _sql.InsertProposalAsync(proposal, ct);

        var createEvent = ApprovalWorkflowPolicy.CreateStateChangeEvent(proposalId, ApprovalStatus.Draft, userEmail, correlationId, now);
        await _sql.InsertEventAsync(createEvent, ct);

        return proposalId;
    }
}

public class ProposeForReviewHandler
{
    private readonly ApprovalWorkflowSql _sql;
    private readonly IClock _clock;

    public ProposeForReviewHandler(ApprovalWorkflowSql sql, IClock clock)
    {
        _sql = sql;
        _clock = clock;
    }

    public async Task Handle(Guid proposalId, string userEmail, Guid correlationId, CancellationToken ct = default)
    {
        var proposal = await _sql.GetProposalAsync(proposalId, ct)
            ?? throw new KeyNotFoundException($"Proposal {proposalId} not found");

        if (!ApprovalWorkflowPolicy.CanProposeForReview(proposal, userEmail))
            throw new UnauthorizedAccessException("Only the proposal's Maker can move it from Draft to Proposed");

        ApprovalWorkflowPolicy.ValidateProposalState(proposal.Status, ApprovalStatus.Proposed);

        await _sql.UpdateProposalStatusAsync(proposalId, ApprovalStatus.Proposed, ct: ct);

        var now = _clock.UtcNow.UtcDateTime;
        var proposeEvent = ApprovalWorkflowPolicy.CreateStateChangeEvent(proposalId, ApprovalStatus.Proposed, userEmail, correlationId, now);
        await _sql.InsertEventAsync(proposeEvent, ct);
    }
}

public class ApproveApprovalHandler
{
    private readonly ApprovalWorkflowSql _sql;
    private readonly IClock _clock;

    public ApproveApprovalHandler(ApprovalWorkflowSql sql, IClock clock)
    {
        _sql = sql;
        _clock = clock;
    }

    public async Task Handle(Guid proposalId, string userEmail, string userRole, string approvalNotes, List<(string Type, string Url, string? Comment)> evidence, Guid correlationId, CancellationToken ct = default)
    {
        var proposal = await _sql.GetProposalAsync(proposalId, ct)
            ?? throw new KeyNotFoundException($"Proposal {proposalId} not found");

        if (!ApprovalWorkflowPolicy.CanApprove(proposal, userEmail, userRole))
            throw new UnauthorizedAccessException("Only Checker role (different from Maker) can approve proposals");

        ApprovalWorkflowPolicy.ValidateProposalState(proposal.Status, ApprovalStatus.Approved);

        await _sql.UpdateProposalStatusAsync(proposalId, ApprovalStatus.Approved, userEmail, approvalNotes, ct);

        var now = _clock.UtcNow.UtcDateTime;
        foreach (var (type, url, comment) in evidence)
        {
            var evt = new ApprovalEvidence
            {
                Id = Guid.NewGuid(),
                ApprovalProposalId = proposalId,
                EvidenceType = type,
                EvidenceUrl = url,
                ReviewerComment = comment,
                PublishedAt = now,
                CorrelationId = correlationId
            };

            await _sql.InsertEvidenceAsync(evt, ct);
        }

        var approvalEvent = ApprovalWorkflowPolicy.CreateStateChangeEvent(proposalId, ApprovalStatus.Approved, userEmail, correlationId, now,
            new Dictionary<string, object> { { "notes", approvalNotes } });
        await _sql.InsertEventAsync(approvalEvent, ct);
    }
}

public class ActivateModelHandler
{
    private readonly ApprovalWorkflowSql _sql;
    private readonly IClock _clock;

    public ActivateModelHandler(ApprovalWorkflowSql sql, IClock clock)
    {
        _sql = sql;
        _clock = clock;
    }

    public async Task Handle(Guid proposalId, string userEmail, string userRole, Guid correlationId, CancellationToken ct = default)
    {
        var proposal = await _sql.GetProposalAsync(proposalId, ct)
            ?? throw new KeyNotFoundException($"Proposal {proposalId} not found");

        if (!ApprovalWorkflowPolicy.CanActivate(proposal, userEmail, userRole))
            throw new UnauthorizedAccessException("Only SRE role can activate approved proposals");

        ApprovalWorkflowPolicy.ValidateProposalState(proposal.Status, ApprovalStatus.Active);

        // DEBT-028: UpdateProposalStatusAsync would overwrite approved_by/approval_notes with
        // the activating SRE's identity and never set activated_by/activated_at. Use the
        // dedicated activation method instead.
        await _sql.ActivateProposalAsync(proposalId, userEmail, ct);

        var activateEvent = ApprovalWorkflowPolicy.CreateStateChangeEvent(proposalId, ApprovalStatus.Active, userEmail, correlationId, _clock.UtcNow.UtcDateTime,
            new Dictionary<string, object> { { "effectiveAt", proposal.EffectiveAt.ToString("O") } });
        await _sql.InsertEventAsync(activateEvent, ct);
    }
}
