using CashLineERP.Contracts.Procurement;

namespace CashLineERP.Application.Procurement;

public sealed class AccountsPayableBillService : IAccountsPayableBillService
{
    private readonly IAccountsPayableBillRepository _repository;

    public AccountsPayableBillService(IAccountsPayableBillRepository repository)
    {
        _repository = repository;
    }

    public IReadOnlyCollection<AccountsPayableBillResponse> GetBills() =>
        _repository.GetBills().Select(Map).ToArray();

    public AccountsPayableBillResponse? GetBill(long accountsPayableBillId)
    {
        var bill = _repository.GetBill(accountsPayableBillId);
        return bill is null ? null : Map(bill);
    }

    public AccountsPayableBillResponse CreateBill(CreateAccountsPayableBillRequest request)
    {
        var bill = _repository.Create(new CreateAccountsPayableBillCommand(
            request.SupplierId,
            request.PurchaseOrderId,
            request.GoodsReceiptId,
            request.PurchaseReturnId,
            request.BillType,
            request.BillDate,
            request.DueDate,
            request.CurrencyCode,
            request.Lines.Select(line => new CreateAccountsPayableBillLineCommand(
                line.PurchaseOrderLineId,
                line.ItemId,
                line.QuantityBilled,
                line.UnitOfMeasureCode,
                line.UnitPrice,
                line.TaxCode)).ToArray()));

        return Map(bill);
    }

    public AccountsPayableBillResponse SubmitBill(long accountsPayableBillId, long actorUserId) =>
        Map(_repository.Submit(accountsPayableBillId, actorUserId));

    public AccountsPayableBillMatchResponse GetBillMatch(long accountsPayableBillId) =>
        Map(_repository.GetBillMatch(accountsPayableBillId));

    public AccountsPayableBillMatchResponse ApproveMatch(long accountsPayableBillId, long actorUserId) =>
        Map(_repository.ApproveMatch(accountsPayableBillId, actorUserId));

    public AccountsPayableBillResponse PlaceBillOnHold(long accountsPayableBillId, long actorUserId, PlaceAccountsPayableBillOnHoldRequest request) =>
        Map(_repository.PlaceOnHold(
            accountsPayableBillId,
            actorUserId,
            new PlaceAccountsPayableBillOnHoldCommand(
                request.ReasonCode,
                request.Notes)));

    public AccountsPayableBillResponse ResolveBillDispute(long accountsPayableBillId, long actorUserId, ResolveAccountsPayableBillDisputeRequest request) =>
        Map(_repository.ResolveDispute(
            accountsPayableBillId,
            actorUserId,
            new ResolveAccountsPayableBillDisputeCommand(
                request.DecisionCode,
                request.Notes)));

    public AccountsPayableBillResponse PostBill(long accountsPayableBillId, long actorUserId) =>
        Map(_repository.Post(accountsPayableBillId, actorUserId));

    private static AccountsPayableBillResponse Map(AccountsPayableBillDetailView bill) =>
        new(
            bill.AccountsPayableBillId,
            bill.BillNumber,
            bill.SupplierId,
            bill.PurchaseOrderId,
            bill.GoodsReceiptId,
            bill.PurchaseReturnId,
            bill.BillType,
            bill.BillDate,
            bill.DueDate,
            bill.CurrencyCode,
            bill.StatusCode,
            bill.ThreeWayMatchStatus,
            bill.DisputeStatusCode,
            bill.DisputeReasonCode,
            bill.DisputeNotes,
            bill.ResolutionDecisionCode,
            bill.ResolutionNotes,
            bill.MatchVarianceAmount,
            bill.MatchNotes,
            bill.GeneralLedgerBatchId,
            bill.SubtotalAmount,
            bill.TaxAmount,
            bill.TotalAmount,
            bill.Lines.Select(line => new AccountsPayableBillLineResponse(
                line.AccountsPayableBillLineId,
                line.LineNumber,
                line.PurchaseOrderLineId,
                line.ItemId,
                line.QuantityBilled,
                line.UnitOfMeasureCode,
                line.UnitPrice,
                line.TaxCode,
                line.LineNetAmount,
                line.LineTaxAmount,
                line.LineTotalAmount)).ToArray());

    private static AccountsPayableBillMatchResponse Map(AccountsPayableBillMatchView match) =>
        new(
            match.AccountsPayableBillId,
            match.BillNumber,
            match.BillStatusCode,
            match.ThreeWayMatchStatus,
            match.IsEligibleForApproval,
            match.IsEligibleForPosting,
            match.BlockingIssueCount,
            match.TotalVarianceAmount,
            match.Summary,
            match.Lines.Select(line => new AccountsPayableBillMatchLineResponse(
                line.AccountsPayableBillLineId,
                line.LineNumber,
                line.PurchaseOrderLineId,
                line.ItemId,
                line.OrderedQuantity,
                line.AcceptedQuantity,
                line.BilledQuantity,
                line.PurchaseOrderUnitPrice,
                line.BilledUnitPrice,
                line.QuantityVariance,
                line.UnitPriceVariance,
                line.AmountVariance,
                line.MatchStatus,
                line.Notes)).ToArray());
}
