using System.ComponentModel.DataAnnotations;
using PowderCoating.Application.DTOs.PrepService;

namespace PowderCoating.Application.DTOs.Quote;

// ============================================================================
// LIST DTO - For Index page listing
// ============================================================================
public class QuoteListDto
{
    public int Id { get; set; }
    public string QuoteNumber { get; set; } = string.Empty;
    public string CustomerOrProspectName { get; set; } = string.Empty;

    // Quote Status (from lookup table)
    public int QuoteStatusId { get; set; }
    public string StatusCode { get; set; } = string.Empty;
    public string StatusDisplayName { get; set; } = string.Empty;
    public string StatusColorClass { get; set; } = "secondary";
    public bool StatusIsDraft { get; set; }

    public DateTime QuoteDate { get; set; }
    public DateTime? ExpirationDate { get; set; }
    public decimal Total { get; set; }
    public string? Notes { get; set; }
    public string? Description { get; set; }
    public string? Tags { get; set; }
    public bool IsExpired => ExpirationDate.HasValue && ExpirationDate.Value < DateTime.UtcNow && StatusIsDraft;
    public bool IsProspect { get; set; }
}

// ============================================================================
// FULL QUOTE DTO - For Details page
// ============================================================================
public class QuoteDto
{
    public int Id { get; set; }
    public string QuoteNumber { get; set; } = string.Empty;

    // Customer or Prospect Info
    public int? CustomerId { get; set; }
    public string? CustomerName { get; set; }
    public string? CustomerCompanyName { get; set; }
    public string? CustomerContactFirstName { get; set; }
    public string? CustomerContactLastName { get; set; }
    public string? CustomerEmail { get; set; }
    public string? CustomerPhone { get; set; }
    public bool CustomerNotifyByEmail { get; set; } = true;
    public string? CustomerMobilePhone { get; set; }
    public bool CustomerNotifyBySms { get; set; }

    public string? ProspectCompanyName { get; set; }
    public string? ProspectContactFirstName { get; set; }
    public string? ProspectContactLastName { get; set; }
    public string? ProspectContactName { get; set; }
    public string? ProspectEmail { get; set; }
    public string? ProspectPhone { get; set; }
    public string? ProspectAddress { get; set; }
    public string? ProspectCity { get; set; }
    public string? ProspectState { get; set; }
    public string? ProspectZipCode { get; set; }
    public bool ProspectSmsConsent { get; set; }
    public DateTime? ProspectSmsConsentedAt { get; set; }

    public string? PreparedById { get; set; }
    public string? PreparedByName { get; set; }
    public string? PreparedByEmail { get; set; }

    // Quote Status (from lookup table)
    public int QuoteStatusId { get; set; }
    public string StatusCode { get; set; } = string.Empty;
    public string StatusDisplayName { get; set; } = string.Empty;
    public string StatusColorClass { get; set; } = "secondary";
    public string? StatusIconClass { get; set; }
    public bool StatusIsApproved { get; set; }
    public bool StatusIsConverted { get; set; }
    public bool StatusIsDraft { get; set; }

    public bool IsCommercial { get; set; }

    // Dates
    public DateTime QuoteDate { get; set; }
    public DateTime? ExpirationDate { get; set; }
    public DateTime? SentDate { get; set; }
    public DateTime? ApprovedDate { get; set; }

    // Pricing
    public decimal SubTotal { get; set; }

    // Discount Information
    public string DiscountType { get; set; } = "None"; // None, Percentage, FixedAmount
    public decimal DiscountValue { get; set; }
    public decimal DiscountPercent { get; set; }
    public decimal DiscountAmount { get; set; }
    public string? DiscountReason { get; set; }
    public bool HideDiscountFromCustomer { get; set; }

    public decimal TaxPercent { get; set; }
    public decimal TaxAmount { get; set; }
    public decimal RushFee { get; set; }
    public decimal Total { get; set; }

    public bool IsRushJob { get; set; }

    // Additional Information
    public string? Description { get; set; }
    public string? Terms { get; set; }
    public string? Notes { get; set; }
    public string? CustomerPO { get; set; }
    public string? ProjectName { get; set; }
    public string? Tags { get; set; }

    // Items
    public List<QuoteItemDto> QuoteItems { get; set; } = new();

    // Prep Services
    public List<PrepServiceDto> PrepServices { get; set; } = new();
    public List<int> PrepServiceIds { get; set; } = new();

    // Pricing Breakdown
    public QuotePricingBreakdownDto? PricingBreakdown { get; set; }

    // Oven selection
    public int? OvenCostId { get; set; }
    public string? OvenLabel { get; set; }

    // Oven batch pricing
    public int OvenBatches { get; set; } = 1;
    public int? OvenCycleMinutes { get; set; }

    // Conversion Tracking
    public int? ConvertedToJobId { get; set; }
    public string? ConvertedToJobNumber { get; set; }

    // Customer Approval Tracking
    public string? ApprovalToken { get; set; }
    public DateTime? ApprovalTokenExpiresAt { get; set; }
    public DateTime? ApprovalTokenUsedAt { get; set; }
    public string? DeclineReason { get; set; }

    public bool IsProspect => !CustomerId.HasValue;
    public bool IsExpired => ExpirationDate.HasValue && ExpirationDate.Value < DateTime.UtcNow && StatusIsDraft;
    public string CustomerOrProspectName => CustomerName ?? ProspectCompanyName ?? ProspectContactName ?? "Unknown";
}

// ============================================================================
// CREATE QUOTE DTO - For creating new quotes
// ============================================================================
public class CreateQuoteDto
{
    // Customer or Prospect Selection
    public bool IsForProspect { get; set; }

    [Display(Name = "Customer")]
    public int? CustomerId { get; set; }

    // Prospect Contact Information
    [Display(Name = "Company Name")]
    [StringLength(100)]
    public string? ProspectCompanyName { get; set; }

    [Display(Name = "Contact Name")]
    [StringLength(100)]
    public string? ProspectContactName { get; set; }

    [Display(Name = "Email")]
    [EmailAddress]
    [StringLength(100)]
    public string? ProspectEmail { get; set; }

    [Display(Name = "Phone")]
    [Phone]
    [StringLength(20)]
    public string? ProspectPhone { get; set; }

    [Display(Name = "Address")]
    [StringLength(200)]
    public string? ProspectAddress { get; set; }

    [Display(Name = "City")]
    [StringLength(100)]
    public string? ProspectCity { get; set; }

    [Display(Name = "State")]
    [StringLength(2)]
    public string? ProspectState { get; set; }

    [Display(Name = "Zip Code")]
    [StringLength(10)]
    public string? ProspectZipCode { get; set; }

    [Display(Name = "SMS Consent")]
    public bool ProspectSmsConsent { get; set; } = false;

    // Oven Selection
    [Display(Name = "Oven")]
    public int? OvenCostId { get; set; }

    // Oven Batch Pricing
    [Display(Name = "Oven Batches")]
    [Range(1, 9999)]
    public int OvenBatches { get; set; } = 1;

    [Display(Name = "Oven Cycle Time (min)")]
    [Range(1, 1440)]
    public int? OvenCycleMinutes { get; set; }

    // Quote Information
    [Display(Name = "Commercial Quote")]
    public bool IsCommercial { get; set; }

    [Display(Name = "Rush Job")]
    public bool IsRushJob { get; set; }

    [Display(Name = "Quote Date")]
    [DataType(DataType.Date)]
    public DateTime QuoteDate { get; set; } = DateTime.Today;

    [Display(Name = "Expiration Date")]
    [DataType(DataType.Date)]
    public DateTime? ExpirationDate { get; set; }

    [Display(Name = "Description")]
    [StringLength(500)]
    public string? Description { get; set; }

    [Display(Name = "Terms & Conditions")]
    [DataType(DataType.MultilineText)]
    public string? Terms { get; set; }

    [Display(Name = "Internal Notes")]
    [DataType(DataType.MultilineText)]
    public string? Notes { get; set; }

    [Display(Name = "Customer PO Number")]
    [StringLength(50)]
    public string? CustomerPO { get; set; }
    public string? ProjectName { get; set; }

    [Display(Name = "Tags")]
    [StringLength(500)]
    public string? Tags { get; set; }

    // Pricing
    [Display(Name = "Tax Percent (%)")]
    [Range(0, 100)]
    public decimal TaxPercent { get; set; }

    // Discount
    [Display(Name = "Discount Type")]
    public string DiscountType { get; set; } = "None"; // None, Percentage, FixedAmount

    [Display(Name = "Discount Value")]
    [Range(0, 999999)]
    public decimal DiscountValue { get; set; }

    [Display(Name = "Discount Reason")]
    [StringLength(200)]
    public string? DiscountReason { get; set; }

    [Display(Name = "Hide discount from customer")]
    public bool HideDiscountFromCustomer { get; set; } = false;

    // Items
    [Required]
    // Note: MinLength validation removed to prevent false positives on initial page load
    // JavaScript validation handles this check on form submission
    public List<CreateQuoteItemDto> QuoteItems { get; set; } = new();

    // Prep Services
    [Display(Name = "Preparation Services")]
    public List<int> PrepServiceIds { get; set; } = new();

    // Email Options
    [Display(Name = "Send quote via email")]
    public bool SendEmailToCustomer { get; set; } = false;

    // AI Photo TempIds — list of uploaded temp photo IDs to promote on save
    public List<string> AiPhotoTempIds { get; set; } = new();

    // Quote Photo TempIds — general (non-AI) photos staged on the Create page
    public List<string> QuotePhotoTempIds { get; set; } = new();
    public List<string> QuotePhotoFileNames { get; set; } = new();
}

// ============================================================================
// UPDATE QUOTE DTO - For editing existing quotes
// ============================================================================
public class UpdateQuoteDto
{
    public int Id { get; set; }

    // Customer or Prospect Info (read-only after creation, but included for completeness)
    public int? CustomerId { get; set; }
    public bool IsForProspect { get; set; }

    // Prospect Contact Information
    [Display(Name = "Company Name")]
    [StringLength(100)]
    public string? ProspectCompanyName { get; set; }

    [Display(Name = "Contact Name")]
    [StringLength(100)]
    public string? ProspectContactName { get; set; }

    [Display(Name = "Email")]
    [EmailAddress]
    [StringLength(100)]
    public string? ProspectEmail { get; set; }

    [Display(Name = "Phone")]
    [Phone]
    [StringLength(20)]
    public string? ProspectPhone { get; set; }

    [Display(Name = "Address")]
    [StringLength(200)]
    public string? ProspectAddress { get; set; }

    [Display(Name = "City")]
    [StringLength(100)]
    public string? ProspectCity { get; set; }

    [Display(Name = "State")]
    [StringLength(2)]
    public string? ProspectState { get; set; }

    [Display(Name = "Zip Code")]
    [StringLength(10)]
    public string? ProspectZipCode { get; set; }

    [Display(Name = "SMS Consent")]
    public bool ProspectSmsConsent { get; set; } = false;

    // Oven Selection
    [Display(Name = "Oven")]
    public int? OvenCostId { get; set; }

    // Oven Batch Pricing
    [Display(Name = "Oven Batches")]
    [Range(1, 9999)]
    public int OvenBatches { get; set; } = 1;

    [Display(Name = "Oven Cycle Time (min)")]
    [Range(1, 1440)]
    public int? OvenCycleMinutes { get; set; }

    // Quote Information
    [Display(Name = "Status")]
    public int QuoteStatusId { get; set; }  // FK to lookup table

    [Display(Name = "Commercial Quote")]
    public bool IsCommercial { get; set; }

    [Display(Name = "Rush Job")]
    public bool IsRushJob { get; set; }

    [Display(Name = "Quote Date")]
    [DataType(DataType.Date)]
    public DateTime QuoteDate { get; set; }

    [Display(Name = "Expiration Date")]
    [DataType(DataType.Date)]
    public DateTime? ExpirationDate { get; set; }

    [Display(Name = "Description")]
    [StringLength(500)]
    public string? Description { get; set; }

    [Display(Name = "Terms & Conditions")]
    [DataType(DataType.MultilineText)]
    public string? Terms { get; set; }

    [Display(Name = "Internal Notes")]
    [DataType(DataType.MultilineText)]
    public string? Notes { get; set; }

    [Display(Name = "Customer PO Number")]
    [StringLength(50)]
    public string? CustomerPO { get; set; }
    public string? ProjectName { get; set; }

    [Display(Name = "Tags")]
    [StringLength(500)]
    public string? Tags { get; set; }

    // Pricing
    [Display(Name = "Tax Percent (%)")]
    [Range(0, 100)]
    public decimal TaxPercent { get; set; }

    // Discount
    [Display(Name = "Discount Type")]
    public string DiscountType { get; set; } = "None"; // None, Percentage, FixedAmount

    [Display(Name = "Discount Value")]
    [Range(0, 999999)]
    public decimal DiscountValue { get; set; }

    [Display(Name = "Discount Reason")]
    [StringLength(200)]
    public string? DiscountReason { get; set; }

    [Display(Name = "Hide discount from customer")]
    public bool HideDiscountFromCustomer { get; set; } = false;

    // Items
    [Required]
    // Note: MinLength validation removed to prevent false positives on initial page load
    // JavaScript validation handles this check on form submission
    public List<CreateQuoteItemDto> QuoteItems { get; set; } = new();

    // Prep Services
    [Display(Name = "Preparation Services")]
    public List<int> PrepServiceIds { get; set; } = new();

    // AI Photo TempIds — list of uploaded temp photo IDs to promote on save
    public List<string> AiPhotoTempIds { get; set; } = new();

    // Quote Photo TempIds — general (non-AI) photos staged on the Edit page (not used by direct-upload Edit path,
    // kept for symmetry / future use)
    public List<string> QuotePhotoTempIds { get; set; } = new();
    public List<string> QuotePhotoFileNames { get; set; } = new();
}

// ============================================================================
// QUOTE ITEM DTO - For displaying quote items
// ============================================================================
public class QuoteItemDto
{
    public int Id { get; set; }
    public int QuoteId { get; set; }

    [Display(Name = "Description")]
    public string Description { get; set; } = string.Empty;

    [Display(Name = "Quantity")]
    public decimal Quantity { get; set; }

    [Display(Name = "Surface Area (sq ft)")]
    public decimal SurfaceAreaSqFt { get; set; }

    [Display(Name = "Estimated Minutes")]
    public int EstimatedMinutes { get; set; }

    [Display(Name = "Requires Sandblasting")]
    public bool RequiresSandblasting { get; set; }

    [Display(Name = "Requires Masking")]
    public bool RequiresMasking { get; set; }

    [Display(Name = "Catalog Item")]
    public int? CatalogItemId { get; set; }
    public string? CatalogItemName { get; set; }

    [Display(Name = "Unit Price")]
    public decimal UnitPrice { get; set; }

    [Display(Name = "Total Price")]
    public decimal TotalPrice { get; set; }

    public string? Notes { get; set; }

    public bool IsGenericItem { get; set; }
    public bool IsLaborItem { get; set; }
    public bool IsSalesItem { get; set; }
    public string? Sku { get; set; }
    public decimal? ManualUnitPrice { get; set; }

    // Coating layers
    public List<QuoteItemCoatDto> Coats { get; set; } = new();

    // Preparation services
    public List<CreateQuoteItemPrepServiceDto> PrepServices { get; set; } = new();

    // Part complexity level for calculated items
    public string? Complexity { get; set; }

    public bool IsAiItem { get; set; }

    // Custom formula item
    public bool IsCustomFormulaItem { get; set; }
    public int? CustomItemTemplateId { get; set; }
    public string? FormulaFieldValuesJson { get; set; }

    // Cost breakdown snapshot
    public decimal ItemMaterialCost { get; set; }
    public decimal ItemLaborCost { get; set; }
    public decimal ItemEquipmentCost { get; set; }
}

// ============================================================================
// CREATE QUOTE ITEM DTO - For creating quote items
// ============================================================================
public class CreateQuoteItemDto
{
    // Description is required for calculated items but auto-populated for catalog items
    [Display(Name = "Description")]
    [StringLength(200)]
    public string Description { get; set; } = string.Empty;

    [Required]
    [Display(Name = "Quantity")]
    [Range(0.01, 10000)]
    public decimal Quantity { get; set; } = 1;

    [Required]
    [Display(Name = "Surface Area (sq ft)")]
    [Range(0, 100000)]  // 0 is valid for generic items
    public decimal SurfaceAreaSqFt { get; set; }

    [Required]
    [Display(Name = "Estimated Minutes")]
    [Range(0, 10000, ErrorMessage = "Estimated minutes must be between 0 and 10,000 (0 for catalog items)")]
    public int EstimatedMinutes { get; set; }

    [Display(Name = "Requires Sandblasting")]
    public bool RequiresSandblasting { get; set; }

    [Display(Name = "Requires Masking")]
    public bool RequiresMasking { get; set; }

    [Display(Name = "Catalog Item")]
    public int? CatalogItemId { get; set; }

    [Display(Name = "Notes")]
    [StringLength(500)]
    public string? Notes { get; set; }

    // Generic item — price entered manually, bypasses calculation engine
    public bool IsGenericItem { get; set; }

    [Range(0, 1000000)]
    public decimal? ManualUnitPrice { get; set; }

    // Labor item — Quantity = hours; priced using StandardLaborRate × markup, no coats
    public bool IsLaborItem { get; set; }

    // Sales item — off-the-shelf merchandise (T-shirts, tumblers, etc.)
    public bool IsSalesItem { get; set; }
    public string? Sku { get; set; }

    // Coating layers
    public List<CreateQuoteItemCoatDto> Coats { get; set; } = new();

    // Prep services (each has its own time estimate)
    // For catalog items, prep cost is only included when IncludePrepCost = true
    public List<CreateQuoteItemPrepServiceDto> PrepServices { get; set; } = new();

    // Controls whether prep service labor cost is added to the item price.
    // Always true for calculated items; catalog items default to false (costs baked in)
    // but can be opted in via the wizard toggle.
    public bool IncludePrepCost { get; set; } = true;

    // Part complexity — drives a price multiplier on calculated items only
    // Values: null | "Simple" | "Moderate" | "Complex" | "Extreme"
    public string? Complexity { get; set; }

    // Optional unit price override for catalog items — overrides the catalog default price
    public decimal? PowderCostOverride { get; set; }

    // True when this item was generated via AI photo analysis
    public bool IsAiItem { get; set; }

    // AI-generated standardized tags (comma-separated, e.g. "automotive,tubular")
    public string? AiTags { get; set; }

    // ID of the AiItemPrediction record captured at analysis time (null for non-AI items)
    public int? AiPredictionId { get; set; }

    // Custom formula item routing — see IsCustomFormulaItem in PricingCalculationService
    public bool IsCustomFormulaItem { get; set; }
    public int? CustomItemTemplateId { get; set; }
    public string? FormulaFieldValuesJson { get; set; }
}

// ============================================================================
// QUOTE ITEM PREP SERVICE DTO
// ============================================================================
public class CreateQuoteItemPrepServiceDto
{
    [Required]
    public int PrepServiceId { get; set; }

    [Required]
    [Range(0, 10000)]
    public int EstimatedMinutes { get; set; }

    /// <summary>Blast setup selected in wizard for this sandblasting prep service.</summary>
    public int? BlastSetupId { get; set; }

    // Populated when mapping from entity (for display purposes)
    public string? PrepServiceName { get; set; }
}

// ============================================================================
// PRICING BREAKDOWN DTO - For transparent cost breakdown
// ============================================================================
public class QuotePricingBreakdownDto
{
    // Per-Item Costs
    public decimal MaterialCosts { get; set; }
    public decimal LaborCosts { get; set; }
    public decimal EquipmentCosts { get; set; }
    public decimal ItemSubtotal { get; set; }

    // Quote-Level Costs
    public decimal ItemsSubtotal { get; set; }
    public decimal ShopSuppliesAmount { get; set; }
    public decimal ShopSuppliesPercent { get; set; }

    public decimal OverheadCosts { get; set; }
    public decimal OverheadPercent { get; set; }

    public decimal ProfitMargin { get; set; }
    public decimal ProfitPercent { get; set; }

    public decimal SubtotalBeforeDiscount { get; set; }

    public decimal PricingTierDiscountAmount { get; set; }
    public decimal PricingTierDiscountPercent { get; set; }
    public decimal QuoteDiscountAmount { get; set; }
    public decimal QuoteDiscountPercent { get; set; }

    public decimal DiscountAmount { get; set; }
    public decimal DiscountPercent { get; set; }

    public decimal RushFee { get; set; }

    public decimal SubtotalAfterDiscount { get; set; }

    public decimal TaxAmount { get; set; }
    public decimal TaxPercent { get; set; }

    public decimal OvenBatchCost { get; set; }
    public int OvenBatches { get; set; }
    public int OvenCycleMinutes { get; set; }

    public decimal FacilityOverheadCost { get; set; }
    public decimal FacilityOverheadRatePerHour { get; set; }

    public decimal Total { get; set; }

    // Cost Breakdown Details
    public string CostBreakdownDetails { get; set; } = string.Empty;
}

// ============================================================================
// CONVERT TO CUSTOMER DTO - For prospect-to-customer conversion
// ============================================================================
public class ConvertQuoteToCustomerDto
{
    public int QuoteId { get; set; }
    public string QuoteNumber { get; set; } = string.Empty;

    // Pre-filled from prospect data
    [Display(Name = "Company Name")]
    [StringLength(100)]
    public string? CompanyName { get; set; }

    [Required]
    [Display(Name = "Contact Name")]
    [StringLength(100)]
    public string ContactName { get; set; } = string.Empty;

    [Display(Name = "Email")]
    [EmailAddress]
    [StringLength(100)]
    public string? Email { get; set; }

    [Display(Name = "Phone")]
    [Phone]
    [StringLength(20)]
    public string? Phone { get; set; }

    [Display(Name = "Address")]
    [StringLength(200)]
    public string? Address { get; set; }

    [Display(Name = "City")]
    [StringLength(100)]
    public string? City { get; set; }

    [Display(Name = "State")]
    [StringLength(2)]
    public string? State { get; set; }

    [Display(Name = "Zip Code")]
    [StringLength(10)]
    public string? ZipCode { get; set; }

    // Additional customer fields
    [Display(Name = "Tax ID / EIN")]
    [StringLength(50)]
    public string? TaxId { get; set; }

    [Display(Name = "Credit Limit")]
    [Range(0, 1000000)]
    public decimal CreditLimit { get; set; }

    [Display(Name = "Pricing Tier")]
    public int? PricingTierId { get; set; }

    [Display(Name = "Customer Type")]
    public bool IsCommercial { get; set; } = true;

    [Display(Name = "Payment Terms")]
    [StringLength(100)]
    public string? PaymentTerms { get; set; }

    [Display(Name = "Notes")]
    [DataType(DataType.MultilineText)]
    public string? Notes { get; set; }

    /// <summary>
    /// Staff must explicitly confirm verbal SMS consent before it carries over to the new customer record.
    /// Pre-checked when ProspectSmsConsent was true on the source quote.
    /// </summary>
    [Display(Name = "Customer has given verbal consent to receive SMS notifications")]
    public bool SmsConsent { get; set; }

    /// <summary>Timestamp from the source quote — preserved so the consent record reflects when consent was originally given.</summary>
    public DateTime? ProspectSmsConsentedAt { get; set; }
}

// ============================================================================
// QUOTE ITEM COAT DTOs - For multi-coat support
// ============================================================================

/// <summary>
/// DTO for creating/editing a coating layer on a quote item
/// </summary>
public class CreateQuoteItemCoatDto
{
    [Required]
    [StringLength(100)]
    [Display(Name = "Coat Name")]
    public string CoatName { get; set; } = "Base Coat";

    public int Sequence { get; set; } = 1;

    [Display(Name = "Inventory Powder")]
    public int? InventoryItemId { get; set; }

    [StringLength(100)]
    [Display(Name = "Color Name")]
    public string? ColorName { get; set; }

    [Display(Name = "Vendor")]
    public int? VendorId { get; set; }

    [StringLength(50)]
    [Display(Name = "Color Code")]
    public string? ColorCode { get; set; }

    [StringLength(50)]
    [Display(Name = "Finish")]
    public string? Finish { get; set; }

    [Range(1, 500)]
    [Display(Name = "Coverage (sq ft/lb)")]
    public decimal CoverageSqFtPerLb { get; set; } = 30m;

    [Range(1, 100)]
    [Display(Name = "Transfer Efficiency (%)")]
    public decimal TransferEfficiency { get; set; } = 65m;

    [Range(0, 1000)]
    [Display(Name = "Powder Cost ($/lb)")]
    public decimal? PowderCostPerLb { get; set; }

    [Range(0, 10000)]
    [Display(Name = "Powder to Order (lbs)")]
    public decimal? PowderToOrder { get; set; }

    [StringLength(500)]
    [Display(Name = "Notes")]
    public string? Notes { get; set; }

    /// <summary>
    /// When true, the additional layer labor charge is not applied even if this is not the first coat.
    /// </summary>
    public bool NoExtraLayerCharge { get; set; }

    /// <summary>Platform powder catalog item ID selected via the Custom tab lookup.</summary>
    public int? CatalogItemId { get; set; }

    /// <summary>
    /// When true (and CatalogItemId is set), the server creates a 0-balance IsIncoming inventory
    /// item from the catalog entry so QR codes can be printed while the powder is in transit.
    /// The coat is then linked to that new inventory record.
    /// </summary>
    public bool AddAsIncoming { get; set; }
}

/// <summary>
/// DTO for displaying a coating layer on a quote item
/// </summary>
public class QuoteItemCoatDto
{
    public int Id { get; set; }
    public string CoatName { get; set; } = string.Empty;
    public int Sequence { get; set; }
    public int? InventoryItemId { get; set; }
    public string? InventoryItemName { get; set; }
    public int? VendorId { get; set; }
    public string? VendorName { get; set; }
    public string? ColorName { get; set; }
    public string? ColorCode { get; set; }
    public string? Finish { get; set; }
    public decimal CoverageSqFtPerLb { get; set; }
    public decimal TransferEfficiency { get; set; }
    public decimal? PowderToOrder { get; set; }
    public decimal CoatMaterialCost { get; set; }
    public decimal CoatLaborCost { get; set; }
    public decimal CoatTotalCost { get; set; }
    public bool NoExtraLayerCharge { get; set; }
    public string? Notes { get; set; }
}

// ============================================================================
// PRICING CALCULATION RESULT - Internal use for pricing service
// ============================================================================

/// <summary>
/// Result of calculating costs for a single coating layer
/// </summary>
public class QuoteItemCoatPricingResult
{
    public decimal CoatMaterialCost { get; set; }
    public decimal CoatLaborCost { get; set; }
    public decimal CoatTotalCost { get; set; }
}

public class QuoteItemPricingResult
{
    public decimal MaterialCost { get; set; }
    public decimal LaborCost { get; set; }
    public decimal EquipmentCost { get; set; }
    public decimal ItemSubtotal { get; set; }
    public decimal UnitPrice { get; set; }
    public decimal TotalPrice { get; set; }
}

public class QuotePricingResult
{
    public decimal ItemsSubtotal { get; set; }
    public decimal ShopSuppliesAmount { get; set; }
    public decimal ShopSuppliesPercent { get; set; }
    public decimal OverheadCosts { get; set; }
    public decimal OverheadPercent { get; set; }
    public decimal ProfitMargin { get; set; }
    public decimal ProfitPercent { get; set; }
    public decimal SubtotalBeforeDiscount { get; set; }

    // Pricing Tier Discount (from customer's pricing tier)
    public decimal PricingTierDiscountAmount { get; set; }
    public decimal PricingTierDiscountPercent { get; set; }

    // Quote-Level Discount (applied on the quote)
    public decimal QuoteDiscountAmount { get; set; }
    public decimal QuoteDiscountPercent { get; set; }

    // Combined Discount (for backward compatibility)
    public decimal DiscountAmount { get; set; }
    public decimal DiscountPercent { get; set; }

    public decimal SubtotalAfterDiscount { get; set; }
    public decimal RushFee { get; set; }
    public decimal TaxAmount { get; set; }
    public decimal TaxPercent { get; set; }
    public decimal Total { get; set; }

    // Oven batch cost (quote-level, separate from per-item calculations)
    public decimal OvenBatchCost { get; set; }
    public int OvenBatches { get; set; }
    public int OvenCycleMinutes { get; set; }

    // Facility overhead (rent + utilities apportioned by estimated job hours)
    public decimal FacilityOverheadCost { get; set; }
    public decimal FacilityOverheadRatePerHour { get; set; }

    // Detailed breakdown for transparency
    public decimal MaterialCosts { get; set; }
    public decimal LaborCosts { get; set; }
    public decimal EquipmentCosts { get; set; }

    // Per-item results (same order as input items)
    public List<QuoteItemPricingResult> ItemResults { get; set; } = new();

    // Pending Custom Powder Order preview — populated only when no "Custom Powder Order" item
    // exists yet (first save scenario). Amount and color list let the UI show a preview row.
    public decimal CustomPowderOrderAmount { get; set; }
    public List<string> CustomPowderOrderColors { get; set; } = new();
}
