using System.ComponentModel.DataAnnotations;
using ERPCore.Domain.Enums;

namespace ERPCore.Dtos.Grn;

// Responses (docs/11 §4) --------------------------------------------------------

public sealed record GrnLineWarrantyNumberDto(string WarrantyNo, int WarrantyPeriodMonths);

public sealed record GrnLineDto(
    int GrnLineId, int? PoLineId, int ItemId, int? BinId,
    decimal Qty, decimal UnitCost, decimal? PoUnitPrice,
    decimal DiscountPct, decimal NetUnitCost, decimal VatPct, decimal VatAmount,
    decimal ReceivedValue, decimal LineTotal, decimal PriceVariance,
    HoldStatus HoldStatus, int? BatchId, IReadOnlyList<GrnLineWarrantyNumberDto> WarrantyNumbers);

public sealed record GrnDto(
    int GrnId, string DocNo, int? PoId, int VendorId, int WarehouseId, GrnStatus Status,
    int CreatedBy, DateTime CreatedAt, DateTime? PostedAt, string? GlJournalNo,
    decimal PaidAmount, decimal BalanceAmount, IReadOnlyList<GrnLineDto> Lines);

/// <summary>Row shape for <c>GET /grns</c> — line count instead of the lines themselves.</summary>
public sealed record GrnSummaryDto(
    int GrnId, string DocNo, int? PoId, int VendorId, int WarehouseId, GrnStatus Status,
    int CreatedBy, DateTime CreatedAt, DateTime? PostedAt, int LineCount,
    decimal PaidAmount, decimal BalanceAmount);

public sealed record CreatedLayerDto(
    int LayerId, int ItemId, int WarehouseId, int? BatchId,
    decimal QtyReceived, decimal QtyRemaining, decimal UnitCost, DateTime ReceiptDate);

public sealed record GrnConfirmResultDto(
    int GrnId, GrnStatus Status, DateTime PostedAt, string GlJournalNo, decimal BalanceAmount,
    IReadOnlyList<CreatedLayerDto> CreatedLayers, IReadOnlyList<int> LedgerRefs, PurchaseOrderStatus? PoStatus);

public sealed record ReleaseLineResultDto(int GrnLineId, HoldStatus HoldStatus);

public sealed record GrnPaymentDto(
    int GrnPaymentId, int GrnId, decimal Amount, DateTime PaymentDate,
    long GlBankAccountId, string BankAccountName, string? Reference, string? GlJournalNo, DateTime CreatedAt);

public sealed class CreateGrnPaymentRequest
{
    [Range(0.01, double.MaxValue)] public decimal Amount { get; set; }
    [Required] public long GlBankAccountId { get; set; }
    [StringLength(100)] public string? Reference { get; set; }
}

// Requests ----------------------------------------------------------------------

public sealed class BatchInput
{
    [Required, StringLength(50)] public string BatchNo { get; set; } = string.Empty;
    public DateOnly? ExpiryDate { get; set; }
}

public sealed class CreateGrnLineInput
{
    /// <summary>Set for a PO-based receipt; cost is then derived from the PO line (02-SECURITY C.3).</summary>
    public int? PoLineId { get; set; }
    [Required] public int ItemId { get; set; }
    public int? BinId { get; set; }
    [Range(0.0001, double.MaxValue)] public decimal Qty { get; set; }
    /// <summary>
    /// Gross unit cost. Required for direct (no-PO) receipts. For a PO line it is an optional
    /// per-receipt price override — when 0/omitted the PO line price is used; when supplied it
    /// wins and a variance is recorded against the PO snapshot (docs/02-SECURITY C.3, revised).
    /// </summary>
    [Range(0, double.MaxValue)] public decimal UnitCost { get; set; }
    /// <summary>Trade discount percentage (0–100). Reduces the inventory cost.</summary>
    [Range(0, 100)] public decimal DiscountPct { get; set; }
    /// <summary>VAT percentage (0–100). Recoverable — does not affect stock value.</summary>
    [Range(0, 100)] public decimal VatPct { get; set; }
    [EnumDataType(typeof(HoldStatus))] public HoldStatus HoldStatus { get; set; } = HoldStatus.Available;
    public BatchInput? Batch { get; set; }
    /// <summary>
    /// Required, one per unit (count must equal <see cref="Qty"/>), when the item is
    /// warranty-tracked (<c>Item.Warranty == Warranty.Warranty</c>). Ignored otherwise.
    /// </summary>
    public List<string>? WarrantyNumbers { get; set; }
}

public sealed class CreateGrnRequest
{
    /// <summary>PO to receive against; null for a direct/emergency receipt (FR-GRN-02).</summary>
    public int? PoId { get; set; }
    /// <summary>Required for a direct receipt (no PO); ignored otherwise (vendor derives from the PO).</summary>
    public int? VendorId { get; set; }
    [Required] public int WarehouseId { get; set; }
    [Required, MinLength(1)] public List<CreateGrnLineInput> Lines { get; set; } = new();
}

public sealed class ReleaseLineRequest
{
    /// <summary><c>Release</c> makes the stock available; <c>Reject</c> removes it from on-hand.</summary>
    [Required, RegularExpression("Release|Reject")]
    public string Action { get; set; } = "Release";
}
