using Microsoft.EntityFrameworkCore;
using PowderCoating.Core.Entities;

namespace PowderCoating.Infrastructure.Services;

public partial class SeedDataService
{
    /// <summary>
    /// Seeds 35 realistic quotes spanning the full status lifecycle: Draft, Sent, Approved,
    /// Rejected, and Expired — with Approved as the clear majority so that SeedJobsAsync
    /// has enough linked quotes to demonstrate the quote-to-job conversion workflow.
    /// </summary>
    /// <remarks>
    /// <para>
    /// Customers are loaded in Id-ascending order (matching the job seeder) and a
    /// shuffled visit schedule interleaves commercial and individual customers so that
    /// statuses and customer types are naturally mixed rather than appearing in blocks.
    /// </para>
    /// <para>
    /// Status pool (fixed seed 55): Approved &times;18, Sent &times;8, Draft &times;4,
    /// Rejected &times;3, Expired &times;2. Shuffled with Fisher-Yates (fixed seed 55)
    /// so every reset produces the same interleaved sequence.
    /// </para>
    /// <para>
    /// Per-customer quote counts mirror real business activity: top commercial accounts
    /// (Carolina Fabrication, Apex, Triangle) generate the most quotes; prospect customers
    /// (Jennifer Clark, Lisa Anderson, Michelle Brown) have quotes but no jobs.
    /// </para>
    /// <para>
    /// Dates spread over 7–185 days back (roughly 6 months) with a small jitter term so
    /// historical charts show a natural activity curve without obviously linear spacing.
    /// </para>
    /// </remarks>
    private async Task<int> SeedQuotesAsync(Company company)
    {
        var existingCount = await _context.Set<Quote>()
            .IgnoreQueryFilters()
            .CountAsync(q => q.CompanyId == company.Id && !q.IsDeleted);

        if (existingCount > 0)
            return 0;

        var quoteStatuses = await _context.Set<QuoteStatusLookup>()
            .IgnoreQueryFilters()
            .Where(s => s.CompanyId == company.Id)
            .ToDictionaryAsync(s => s.StatusCode, s => s.Id);

        if (quoteStatuses.Count == 0)
            return 0;

        // Load in Id order — same ordering as the job seeder so CustomerProfile(ci) indices align
        var allCustomers = await _context.Set<Customer>()
            .IgnoreQueryFilters()
            .Where(c => c.CompanyId == company.Id && !c.IsDeleted)
            .OrderBy(c => c.Id)
            .ToListAsync();

        if (allCustomers.Count == 0)
            return 0;

        var preparedByUser = await _userManager.Users
            .Where(u => u.CompanyId == company.Id)
            .FirstOrDefaultAsync();

        var now = DateTime.UtcNow;
        var prefix = $"QT-{now:yy}{now.Month:D2}-";
        var existing = await _context.Set<Quote>()
            .IgnoreQueryFilters()
            .Where(q => q.QuoteNumber.StartsWith(prefix))
            .Select(q => q.QuoteNumber)
            .ToListAsync();
        var maxNum = 0;
        foreach (var n in existing)
            if (n.Length >= 12 && int.TryParse(n.Substring(8, 4), out var x) && x > maxNum) maxNum = x;
        var seq = maxNum + 1;

        // ── Per-customer quote counts (must sum to 35) ─────────────────────────
        // Indices match the Id-ascending customer list from SeedCustomersAsync:
        //   0=Carolina Fabrication, 1=Apex Motorsports, 2=Triangle Offroad,
        //   3=Smith Welding, 4=Raleigh Architectural, 5=East Coast Powderworks,
        //   6=Piedmont Metal Works, 7=Cary Industrial, 8=Durham Tech, 9=Wake County Fleet,
        //   10–26 = individual residential customers
        static int QuotesFor(int ci) => ci switch
        {
            0  => 4,  // Carolina Fabrication — most quotes
            1  => 3,  // Apex Motorsports
            2  => 3,  // Triangle Offroad
            3  => 3,  // Smith Welding
            4  => 2,  // Raleigh Architectural Metals
            5  => 2,  // East Coast Powderworks
            6  => 1,  // Piedmont Metal Works
            7  => 1,  // Cary Industrial Solutions
            8  => 2,  // Durham Tech Equipment
            9  => 3,  // Wake County Fleet Services
            10 => 1,  // John Davis
            11 => 1,  // Sarah Jenkins
            12 => 1,  // Mike Thompson
            13 => 1,  // Robert Miller
            14 => 1,  // Jennifer Clark — prospect (quote only, no job)
            15 => 1,  // David Wilson
            16 => 1,  // Lisa Anderson — prospect
            17 => 1,  // Thomas Harris
            19 => 1,  // James Taylor
            20 => 1,  // Michelle Brown — prospect
            21 => 1,  // Chris Lee
            _  => 0,
        };

        // ── Status pool: realistic distribution (total = 35) ───────────────────
        // Approved is the majority; Rejected and Expired are rare.
        var statusPool = new List<string>();
        foreach (var (code, count) in new (string Code, int Count)[]
        {
            ("APPROVED", 18),
            ("SENT",      8),
            ("DRAFT",     4),
            ("REJECTED",  3),
            ("EXPIRED",   2),
        })
        {
            for (int k = 0; k < count; k++) statusPool.Add(code);
        }
        // Fisher-Yates shuffle — fixed seed for deterministic resets
        var statusRng = new Random(55);
        for (int k = statusPool.Count - 1; k > 0; k--)
        {
            var swap = statusRng.Next(k + 1);
            (statusPool[k], statusPool[swap]) = (statusPool[swap], statusPool[k]);
        }

        // ── Customer visit schedule: interleave commercial (ci 0–9) and individual (ci 10+) ──
        // Same two-pool approach as the job seeder — prevents commercial customers from
        // clustering at the top of any list sorted by quote number or date.
        var commercialVisits = new List<int>();
        var individualVisits = new List<int>();
        for (int ci = 0; ci < allCustomers.Count; ci++)
        {
            var count = QuotesFor(ci);
            for (int j = 0; j < count; j++)
                (ci < 10 ? commercialVisits : individualVisits).Add(ci);
        }
        var rngC = new Random(42);
        for (int k = commercialVisits.Count - 1; k > 0; k--)
        {
            var swap = rngC.Next(k + 1);
            (commercialVisits[k], commercialVisits[swap]) = (commercialVisits[swap], commercialVisits[k]);
        }
        var rngI = new Random(17);
        for (int k = individualVisits.Count - 1; k > 0; k--)
        {
            var swap = rngI.Next(k + 1);
            (individualVisits[k], individualVisits[swap]) = (individualVisits[swap], individualVisits[k]);
        }
        var visitSchedule  = new List<int>(commercialVisits.Count + individualVisits.Count);
        double indStride   = individualVisits.Count > 0
            ? (commercialVisits.Count + 1.0) / (individualVisits.Count + 1.0)
            : double.MaxValue;
        int indInsertIdx = 0;
        for (int comIdx = 0; comIdx < commercialVisits.Count; comIdx++)
        {
            while (indInsertIdx < individualVisits.Count && (indInsertIdx + 1) * indStride <= comIdx + 1)
                visitSchedule.Add(individualVisits[indInsertIdx++]);
            visitSchedule.Add(commercialVisits[comIdx]);
        }
        while (indInsertIdx < individualVisits.Count)
            visitSchedule.Add(individualVisits[indInsertIdx++]);

        // ── Item catalogue — 15 common powder coating jobs ──────────────────────
        static (string desc, string color, bool sand, bool mask, int mins, decimal sqft) ItemSpec(int i) =>
            (i % 15) switch
            {
                0  => ("18\" Aluminum Wheels (set of 4)",          "Gloss Black",     false, false, 45, 12.0m),
                1  => ("17\" Steel Wheels (set of 4)",             "Signal White",    false, false, 30,  8.5m),
                2  => ("Jeep Bumper & Rock Sliders",               "Matte Black",     true,  false, 60, 15.0m),
                3  => ("Motorcycle Frame",                         "Matte Black",     true,  false, 90, 14.0m),
                4  => ("Steel Shelving Units (10-shelf set)",      "Textured Gray",   true,  false, 55, 18.0m),
                5  => ("Industrial Machine Guard Panels",          "Safety Yellow",   false, false, 35, 20.0m),
                6  => ("Aluminum Window Frames (set of 8)",        "Satin Bronze",    false, true,  50, 22.0m),
                7  => ("Steel Handrail System — 40 ft",       "Gloss Black",     true,  false, 120, 40.0m),
                8  => ("Wrought Iron Entry Gate",                  "Hammered Black",  true,  false, 180, 35.0m),
                9  => ("Brake Calipers (set of 4)",               "Candy Red",       false, true,  35,  4.0m),
                10 => ("Restaurant Chair Frames (set of 20)",      "Hammered Bronze", false, false, 60, 30.0m),
                11 => ("Bicycle Frame",                            "Candy Red",       true,  true,  60,  6.5m),
                12 => ("Compressor Tank",                          "Safety Orange",   true,  false, 45, 10.0m),
                13 => ("Patio Furniture Set (6 pieces)",           "Textured Beige",  false, false, 50, 24.0m),
                _  => ("Custom Steel Fabrication — Batch",    "Matte Black",     true,  false, 40, 15.0m)
            };

        var quotes         = new List<Quote>();
        var quotesByCustomer = new int[allCustomers.Count]; // within-customer quote counter

        for (int visitIdx = 0; visitIdx < visitSchedule.Count; visitIdx++)
        {
            var ci        = visitSchedule[visitIdx];
            var customer  = allCustomers[ci];
            var j         = quotesByCustomer[ci]++;   // within-customer quote index (unused except for date jitter)
            var statusCode = statusPool[visitIdx];

            // Dates spread 7–185 days back; small jitter via ci so identical-status quotes
            // for the same customer don't land on exactly the same day.
            var daysAgo   = Math.Max(7, 185 - visitIdx * 5 + (ci % 5));
            var quoteDate  = now.AddDays(-daysAgo);
            var expireDate = quoteDate.AddDays(30);

            var itemCount = 1 + (visitIdx % 3);
            var items = new List<QuoteItem>();

            for (int k = 0; k < itemCount; k++)
            {
                var (desc, color, sand, mask, mins, sqft) = ItemSpec(visitIdx * 3 + k);
                var qty      = 1 + (k % 4);
                var tierMult = 1m - ((customer.PricingTier?.DiscountPercent ?? 0m) / 100m);
                var unitPrice = Math.Round(sqft * 8.50m * tierMult + (visitIdx % 6) * 5.0m, 2);

                items.Add(new QuoteItem
                {
                    Description          = desc,
                    Quantity             = qty,
                    SurfaceAreaSqFt      = sqft * qty,
                    UnitPrice            = unitPrice,
                    TotalPrice           = unitPrice * qty,
                    RequiresSandblasting = sand,
                    RequiresMasking      = mask,
                    EstimatedMinutes     = mins,
                    Complexity           = (visitIdx % 4) switch { 0 => "Simple", 1 => "Moderate", 2 => "Complex", _ => "Simple" },
                    Notes                = k == 0 && visitIdx % 5 == 0 ? $"Customer requested {color} — confirm shade before run." : null,
                    CompanyId            = company.Id,
                    CreatedAt            = quoteDate
                });
            }

            var subtotal      = items.Sum(it => it.TotalPrice);
            var discountPct   = customer.PricingTier?.DiscountPercent ?? 0m;
            var discountAmt   = Math.Round(subtotal * discountPct / 100m, 2);
            var afterDiscount = subtotal - discountAmt;
            var taxPct        = customer.IsTaxExempt ? 0m : 7.5m;
            var taxAmt        = Math.Round(afterDiscount * taxPct / 100m, 2);
            var rushFee       = visitIdx % 10 == 0 ? Math.Round(afterDiscount * 0.15m, 2) : 0m;
            var total         = afterDiscount + taxAmt + rushFee;

            quotes.Add(new Quote
            {
                QuoteNumber      = $"{prefix}{seq:D4}",
                CustomerId       = customer.Id,
                PreparedById     = preparedByUser?.Id,
                QuoteStatusId    = quoteStatuses[statusCode],
                IsCommercial     = customer.IsCommercial,
                IsRushJob        = visitIdx % 10 == 0,
                QuoteDate        = quoteDate,
                ExpirationDate   = expireDate,
                SentDate         = statusCode != "DRAFT"    ? quoteDate.AddDays(1) : null,
                ApprovedDate     = statusCode == "APPROVED" ? quoteDate.AddDays(4) : null,
                ItemsSubtotal    = subtotal,
                SubTotal         = subtotal,
                DiscountPercent  = discountPct,
                DiscountAmount   = discountAmt,
                TaxPercent       = taxPct,
                TaxAmount        = taxAmt,
                RushFee          = rushFee,
                Total            = total,
                Description      = $"Powder coating services — {items[0].Description.Split('(')[0].TrimEnd()}",
                Terms            = customer.PaymentTerms ?? "Net 30",
                Notes            = visitIdx % 8  == 0 ? "Customer requested color sample before full production run." :
                                   visitIdx % 13 == 0 ? "Rush turnaround requested — 3 business days." : null,
                CustomerPO       = customer.IsCommercial && visitIdx % 2 == 0 ? $"PO-{30000 + visitIdx}" : null,
                RequiresDeposit  = visitIdx % 4 == 0,
                DepositPercent   = visitIdx % 4 == 0 ? 50m : 0m,
                QuoteItems       = items,
                CompanyId        = company.Id,
                CreatedAt        = quoteDate,
                UpdatedAt        = statusCode == "DRAFT" ? quoteDate : quoteDate.AddDays(1)
            });

            seq++;
        }

        await _context.Set<Quote>().AddRangeAsync(quotes);
        await _context.SaveChangesAsync();

        return quotes.Count;
    }
}
