using CashLineERP.Contracts.Sales;

namespace CashLineERP.Application.Sales;

public sealed class SalesOrderService : ISalesOrderService
{
    private readonly ISalesOrderRepository _repository;

    public SalesOrderService(ISalesOrderRepository repository)
    {
        _repository = repository;
    }

    public IReadOnlyCollection<SalesOrderResponse> GetSalesOrders() =>
        _repository.GetSalesOrders().Select(Map).ToArray();

    public SalesOrderResponse? GetSalesOrder(long salesOrderId)
    {
        var salesOrder = _repository.GetSalesOrder(salesOrderId);
        return salesOrder is null ? null : Map(salesOrder);
    }

    public SalesOrderResponse CreateSalesOrder(CreateSalesOrderRequest request)
    {
        var salesOrder = _repository.Create(new CreateSalesOrderCommand(
            request.BranchId,
            request.CustomerId,
            request.OrderDate,
            request.CurrencyCode,
            request.ExchangeRate,
            request.Lines.Select(line => new CreateSalesOrderLineCommand(
                line.ItemId,
                line.WarehouseId,
                line.OrderedQuantity,
                line.UnitOfMeasureCode,
                line.UnitPrice,
                line.TaxCode)).ToArray()));

        return Map(salesOrder);
    }

    public SalesOrderResponse SubmitSalesOrder(long salesOrderId, long actorUserId) =>
        Map(_repository.Submit(salesOrderId, actorUserId));

    public SalesOrderResponse ReleaseCreditHold(long salesOrderId, ReleaseSalesOrderCreditHoldRequest request) =>
        Map(_repository.ReleaseCreditHold(salesOrderId, request.ActorUserId, request.DecisionCode, request.Notes));

    public IReadOnlyCollection<SalesDiscountOverrideResponse> GetDiscountOverrides(long salesOrderId) =>
        _repository.GetDiscountOverrides(salesOrderId).Select(Map).ToArray();

    public SalesDiscountOverrideResponse CreateDiscountOverride(long salesOrderId, long actorUserId, CreateSalesDiscountOverrideRequest request) =>
        Map(_repository.CreateDiscountOverride(salesOrderId, actorUserId, new CreateSalesDiscountOverrideCommand(
            request.SalesOrderLineId,
            request.RequestedUnitPrice,
            request.ReasonCode,
            request.Notes)));

    public SalesDiscountOverrideResponse ApproveDiscountOverride(long salesOrderId, long actorUserId, ReviewSalesDiscountOverrideRequest request, long salesDiscountOverrideId) =>
        Map(_repository.ApproveDiscountOverride(salesOrderId, salesDiscountOverrideId, actorUserId, new ReviewSalesDiscountOverrideCommand(request.DecisionNotes)));

    public SalesDiscountOverrideResponse RejectDiscountOverride(long salesOrderId, long actorUserId, ReviewSalesDiscountOverrideRequest request, long salesDiscountOverrideId) =>
        Map(_repository.RejectDiscountOverride(salesOrderId, salesDiscountOverrideId, actorUserId, new ReviewSalesDiscountOverrideCommand(request.DecisionNotes)));

    public CustomerCreditControlResponse GetCustomerCreditControl(long customerId, string? currencyCode = null, DateOnly? asOfDate = null)
    {
        var normalizedCurrencyCode = string.IsNullOrWhiteSpace(currencyCode) ? "EGP" : currencyCode.Trim().ToUpperInvariant();
        var effectiveDate = asOfDate ?? DateOnly.FromDateTime(DateTime.UtcNow);
        return Map(_repository.GetCustomerCreditControl(customerId, normalizedCurrencyCode, effectiveDate));
    }

    private static SalesOrderResponse Map(SalesOrderDetailView salesOrder) =>
        new(
            salesOrder.SalesOrderId,
            salesOrder.SalesOrderNumber,
            salesOrder.BranchId,
            salesOrder.CustomerId,
            salesOrder.OrderDate,
            salesOrder.CurrencyCode,
            salesOrder.ExchangeRate,
            salesOrder.StatusCode,
            salesOrder.CreditStatusCode,
            salesOrder.CreditHoldReason,
            salesOrder.CreditLimitAmountAtCheck,
            salesOrder.CreditExposureAmountAtCheck,
            salesOrder.OverdueInvoiceCountAtCheck,
            salesOrder.CreditCheckedAtUtc,
            salesOrder.CreditReleaseDecisionCode,
            salesOrder.CreditReleaseNotes,
            salesOrder.CreditReleasedAtUtc,
            salesOrder.CreditReleasedByUserId,
            salesOrder.SubtotalAmount,
            salesOrder.TaxAmount,
            salesOrder.TotalAmount,
            salesOrder.Lines.Select(line => new SalesOrderLineResponse(
                line.SalesOrderLineId,
                line.LineNumber,
                line.ItemId,
                line.WarehouseId,
                line.OrderedQuantity,
                line.AllocatedQuantity,
                line.DeliveredQuantity,
                line.UnitOfMeasureCode,
                line.UnitPrice,
                line.BaseUnitPrice,
                line.PricingRuleId,
                line.PricingRuleCode,
                line.PricingSourceCode,
                line.DiscountAmount,
                line.DiscountPercent,
                line.DiscountOverrideId,
                line.DiscountSourceCode,
                line.TaxCode,
                line.LineNetAmount,
                line.LineTaxAmount,
                line.LineTotalAmount)).ToArray());

    private static SalesDiscountOverrideResponse Map(SalesDiscountOverrideDetailView discountOverride) =>
        new(
            discountOverride.SalesDiscountOverrideId,
            discountOverride.OverrideNumber,
            discountOverride.SalesOrderId,
            discountOverride.SalesOrderLineId,
            discountOverride.BaseUnitPrice,
            discountOverride.RequestedUnitPrice,
            discountOverride.RequestedDiscountAmount,
            discountOverride.RequestedDiscountPercent,
            discountOverride.ReasonCode,
            discountOverride.Notes,
            discountOverride.StatusCode,
            discountOverride.SubmittedAtUtc,
            discountOverride.SubmittedByUserId,
            discountOverride.DecidedAtUtc,
            discountOverride.DecidedByUserId,
            discountOverride.DecisionCode,
            discountOverride.DecisionNotes);

    private static CustomerCreditControlResponse Map(CustomerCreditControlView control) =>
        new(
            control.CustomerId,
            control.CustomerCode,
            control.CustomerName,
            control.CurrencyCode,
            control.BaseCurrencyCode,
            control.CreditLimitAmount,
            control.CreditLimitBaseAmount,
            control.CreditTermsDays,
            control.IsAccountCreditHoldEnabled,
            control.AccountCreditHoldReason,
            control.OutstandingReceivablesAmount,
            control.OutstandingReceivablesBaseAmount,
            control.OpenSalesOrderAmount,
            control.OpenSalesOrderBaseAmount,
            control.CurrentExposureAmount,
            control.CurrentExposureBaseAmount,
            control.OverdueInvoiceCount,
            control.OldestOverdueInvoiceDate,
            control.ExposureIncludesMultipleCurrencies,
            control.ShouldHoldNewOrders,
            control.HoldReason);
}
