using CashLineERP.Application.Procurement;
using CashLineERP.Domain.Procurement;
using CashLineERP.Domain.Workflow;
using CashLineERP.SharedKernel.Workflow;
using Microsoft.EntityFrameworkCore;

namespace CashLineERP.Infrastructure.Persistence.Repositories;

public sealed class PurchaseRequisitionRepository : IPurchaseRequisitionRepository
{
    private readonly CashLineErpDbContext _dbContext;

    public PurchaseRequisitionRepository(CashLineErpDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public IReadOnlyCollection<PurchaseRequisitionDetailView> GetPurchaseRequisitions()
    {
        return _dbContext.PurchaseRequisitions
            .AsNoTracking()
            .Include(requisition => requisition.Lines)
            .OrderByDescending(requisition => requisition.RequisitionDate)
            .ThenByDescending(requisition => requisition.Id)
            .Select(Map)
            .ToArray();
    }

    public PurchaseRequisitionDetailView? GetPurchaseRequisition(long requisitionId)
    {
        return _dbContext.PurchaseRequisitions
            .AsNoTracking()
            .Include(requisition => requisition.Lines)
            .Where(requisition => requisition.Id == requisitionId)
            .Select(Map)
            .SingleOrDefault();
    }

    public PurchaseRequisitionDetailView Create(CreatePurchaseRequisitionCommand command)
    {
        var nextSequence = (_dbContext.PurchaseRequisitions.Max(requisition => (long?)requisition.Id) ?? 0L) + 1L;
        var requisitionNumber = $"PR-{nextSequence:D6}";

        var requisition = new PurchaseRequisition(
            requisitionNumber,
            command.BranchId,
            command.RequesterUserId,
            command.RequisitionDate,
            command.NeedByDate,
            command.PurposeText,
            command.CurrencyCode);

        var lineNumber = 1;
        foreach (var line in command.Lines)
        {
            requisition.AddLine(
                lineNumber++,
                line.ItemId,
                line.RequestedQuantity,
                line.UnitOfMeasureCode,
                line.EstimatedUnitPrice,
                line.NeedByDate,
                line.Notes);
        }

        _dbContext.PurchaseRequisitions.Add(requisition);
        _dbContext.SaveChanges();

        return GetPurchaseRequisition(requisition.Id)!;
    }

    public PurchaseRequisitionDetailView Submit(long requisitionId, long actorUserId)
    {
        var requisition = _dbContext.PurchaseRequisitions
            .Include(existingRequisition => existingRequisition.Lines)
            .Single(requisition => requisition.Id == requisitionId);

        requisition.Submit(actorUserId);

        var workflowInstance = new WorkflowInstance(
            moduleCode: "PROCUREMENT",
            documentType: "PURCHASE_REQUISITION",
            documentId: requisition.Id,
            makerUserId: requisition.RequesterUserId,
            workflowRuleId: 1L);
        workflowInstance.Submit(actorUserId, "Purchase requisition submitted from procurement workspace.");

        _dbContext.WorkflowInstances.Add(workflowInstance);
        _dbContext.SaveChanges();

        return GetPurchaseRequisition(requisition.Id)!;
    }

    private static PurchaseRequisitionDetailView Map(PurchaseRequisition requisition)
    {
        return new PurchaseRequisitionDetailView(
            requisition.Id,
            requisition.RequisitionNumber,
            requisition.BranchId,
            requisition.RequesterUserId,
            requisition.RequisitionDate,
            requisition.NeedByDate,
            WorkflowStatusSeedIds.ToCode(requisition.StatusId),
            requisition.PurposeText,
            requisition.CurrencyCode,
            requisition.EstimatedTotalAmount,
            requisition.Lines
                .OrderBy(line => line.LineNumber)
                .Select(line => new PurchaseRequisitionLineView(
                    line.Id,
                    line.LineNumber,
                    line.ItemId,
                    line.RequestedQuantity,
                    line.UnitOfMeasureCode,
                    line.NeedByDate,
                    line.EstimatedUnitPrice,
                    WorkflowStatusSeedIds.ToCode(line.StatusId),
                    line.Notes))
                .ToArray());
    }
}
