using CashLineERP.Domain.Common;
using CashLineERP.SharedKernel.Workflow;

namespace CashLineERP.Domain.Workflow;

public sealed class WorkflowInstance : Entity
{
    public long Id { get; private set; }

    public string ModuleCode { get; private set; } = string.Empty;

    public string DocumentType { get; private set; } = string.Empty;

    public long DocumentId { get; private set; }

    public long? WorkflowRuleId { get; private set; }

    public long MakerUserId { get; private set; }

    public long? CheckerUserId { get; private set; }

    public DateTime? SubmittedAtUtc { get; private set; }

    public DateTime? DecidedAtUtc { get; private set; }

    public string CurrentStatusCode { get; private set; } = WorkflowStatusCode.Draft;

    public List<WorkflowAction> Actions { get; private set; } = [];

    public WorkflowInstance(
        string moduleCode,
        string documentType,
        long documentId,
        long makerUserId,
        long? workflowRuleId = null)
    {
        ModuleCode = moduleCode;
        DocumentType = documentType;
        DocumentId = documentId;
        MakerUserId = makerUserId;
        WorkflowRuleId = workflowRuleId;
    }

    private WorkflowInstance()
    {
    }

    public void Submit(long actorUserId, string? comment = null)
    {
        SubmittedAtUtc = DateTime.UtcNow;
        Transition(actorUserId, "SUBMIT", WorkflowStatusCode.Submitted, comment);
    }

    public void Return(long actorUserId, string? comment = null)
    {
        Transition(actorUserId, "RETURN", WorkflowStatusCode.Returned, comment);
    }

    public void Approve(long actorUserId, string? comment = null)
    {
        CheckerUserId = actorUserId;
        DecidedAtUtc = DateTime.UtcNow;
        Transition(actorUserId, "APPROVE", WorkflowStatusCode.Approved, comment);
    }

    public void Reject(long actorUserId, string? comment = null)
    {
        CheckerUserId = actorUserId;
        DecidedAtUtc = DateTime.UtcNow;
        Transition(actorUserId, "REJECT", WorkflowStatusCode.Rejected, comment);
    }

    public void Hold(long actorUserId, string? comment = null)
    {
        Transition(actorUserId, "HOLD", WorkflowStatusCode.OnHold, comment);
    }

    public void Reopen(long actorUserId, string? comment = null)
    {
        Transition(actorUserId, "REOPEN", WorkflowStatusCode.Draft, comment);
    }

    private void Transition(long actorUserId, string actionCode, string newStatusCode, string? comment)
    {
        Actions.Add(new WorkflowAction(
            workflowInstanceId: Id,
            actionCode: actionCode,
            previousStatusCode: CurrentStatusCode,
            newStatusCode: newStatusCode,
            actorUserId: actorUserId,
            comment: comment));

        CurrentStatusCode = newStatusCode;
        Touch(actorUserId);
    }
}
