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 RfqRepository : IRfqRepository
{
    private readonly CashLineErpDbContext _dbContext;

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

    public IReadOnlyCollection<RfqDetailView> GetRfqs()
    {
        var rfqs = _dbContext.RequestForQuotations
            .AsNoTracking()
            .Include(rfq => rfq.Lines)
            .Include(rfq => rfq.Suppliers)
            .OrderByDescending(rfq => rfq.RfqDate)
            .ThenByDescending(rfq => rfq.Id)
            .ToArray();

        return rfqs.Select(Map).ToArray();
    }

    public RfqDetailView? GetRfq(long rfqId)
    {
        var rfq = _dbContext.RequestForQuotations
            .AsNoTracking()
            .Include(existingRfq => existingRfq.Lines)
            .Include(existingRfq => existingRfq.Suppliers)
            .SingleOrDefault(existingRfq => existingRfq.Id == rfqId);

        return rfq is null ? null : Map(rfq);
    }

    public RfqDetailView Create(CreateRfqCommand command)
    {
        var nextSequence = (_dbContext.RequestForQuotations.Max(rfq => (long?)rfq.Id) ?? 0L) + 1L;
        var rfq = new RequestForQuotation(
            rfqNumber: $"RFQ-{nextSequence:D6}",
            branchId: command.BranchId,
            departmentId: command.DepartmentId,
            purchaseRequisitionId: command.PurchaseRequisitionId,
            requesterUserId: command.RequesterUserId,
            buyerUserId: command.BuyerUserId,
            rfqType: command.RfqType,
            rfqDate: command.RfqDate,
            validUntilDate: command.ValidUntilDate,
            currencyCode: command.CurrencyCode,
            notes: command.Notes);

        var lineNumber = 1;
        foreach (var line in command.Lines)
        {
            rfq.AddLine(
                lineNumber++,
                line.PurchaseRequisitionLineId,
                line.ItemId,
                line.ItemDescription,
                line.RequestedQuantity,
                line.UnitOfMeasureCode,
                line.NeedByDate,
                line.TaxCode,
                line.TargetUnitPrice,
                line.Notes);
        }

        foreach (var supplierId in command.SupplierIds.Distinct())
        {
            rfq.InviteSupplier(supplierId);
        }

        _dbContext.RequestForQuotations.Add(rfq);
        _dbContext.SaveChanges();

        return GetRfq(rfq.Id)!;
    }

    public RfqDetailView Submit(long rfqId, long actorUserId)
    {
        var rfq = _dbContext.RequestForQuotations
            .Include(existingRfq => existingRfq.Lines)
            .Include(existingRfq => existingRfq.Suppliers)
            .Single(existingRfq => existingRfq.Id == rfqId);

        rfq.Submit(actorUserId);

        var workflowInstance = new WorkflowInstance(
            moduleCode: "PROCUREMENT",
            documentType: "RFQ",
            documentId: rfq.Id,
            makerUserId: rfq.RequesterUserId ?? actorUserId,
            workflowRuleId: 1L);
        workflowInstance.Submit(actorUserId, "RFQ submitted for supplier invitation and commercial processing.");

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

        return GetRfq(rfq.Id)!;
    }

    private static RfqDetailView Map(RequestForQuotation rfq) =>
        new(
            rfq.Id,
            rfq.RfqNumber,
            rfq.BranchId,
            rfq.DepartmentId,
            rfq.PurchaseRequisitionId,
            rfq.RequesterUserId,
            rfq.BuyerUserId,
            rfq.RfqType,
            rfq.RfqDate,
            rfq.ValidUntilDate,
            WorkflowStatusSeedIds.ToCode(rfq.StatusId),
            rfq.CurrencyCode,
            rfq.Notes,
            rfq.Lines.OrderBy(line => line.LineNumber).Select(line => new RfqLineView(
                line.Id,
                line.LineNumber,
                line.PurchaseRequisitionLineId,
                line.ItemId,
                line.ItemDescription,
                line.RequestedQuantity,
                line.UnitOfMeasureCode,
                line.NeedByDate,
                line.TaxCode,
                line.TargetUnitPrice,
                line.Notes)).ToArray(),
            rfq.Suppliers.OrderBy(supplier => supplier.SupplierId).Select(supplier => new RfqSupplierView(
                supplier.SupplierId,
                supplier.InviteStatus,
                supplier.InvitedAtUtc,
                supplier.RespondedAtUtc,
                supplier.DeclineReason)).ToArray());
}
