using Indotalent.Infrastructure.Database;
using Indotalent.Data.Entities;
using Indotalent.Data.Enums;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace Indotalent.Features.Root.Home.Cqrs;

// Chart data
public class ChartDataPoint
{
    public string? Label { get; set; }
    public double SalesAmount { get; set; }
    public double PurchaseAmount { get; set; }
}

// WMS-specific DTOs
public class WarehouseKpiDto
{
    public string? WarehouseName { get; set; }
    public double StockQty { get; set; }
    public int SkuCount { get; set; }
    public decimal InventoryValue { get; set; }
    public string? Color { get; set; }
}

public class WmsModuleBreakdownDto
{
    public string? Label { get; set; }
    public int Count { get; set; }
    public double Quantity { get; set; }
    public string? Color { get; set; }
}

public class WmsPipelineSummaryDto
{
    public string? Label { get; set; }
    public string? Value { get; set; }
    public string? IconColorClass { get; set; }
}

public class WmsConversionMetricDto
{
    public string? Label { get; set; }
    public double Current { get; set; }
    public double Target { get; set; }
    public double PctValue { get; set; }
    public string? BarColor { get; set; }
}

public class WmsRecentTransactionDto
{
    public string? Number { get; set; }
    public string? Description { get; set; }
    public string? Module { get; set; }
    public double Quantity { get; set; }
    public string? Type { get; set; }
    public string? Status { get; set; }
    public DateTimeOffset? Date { get; set; }
}

public class WmsActivityFeedDto
{
    public string? Title { get; set; }
    public string? Detail { get; set; }
    public string? TimeAgo { get; set; }
    public string? ChipLabel { get; set; }
    public string? ChipColor { get; set; }
    public string? AvatarBg { get; set; }
    public string? AvatarText { get; set; }
}

public class WmsStockMovementPointDto
{
    public string? Label { get; set; }
    public double InQty { get; set; }
    public double OutQty { get; set; }
}

public class WmsYearlyMovementDto
{
    public string? Category { get; set; }
    public double Qty { get; set; }
    public string? Color { get; set; }
}

public class WmsTopProductDto
{
    public string? ProductName { get; set; }
    public double TotalMovement { get; set; }
    public string? Color { get; set; }
}

public class DashboardWmsResponse
{
    // Core KPIs
    public double TotalStockQty { get; set; }
    public decimal InventoryValue { get; set; }
    public int TotalSku { get; set; }
    public int WarehouseCount { get; set; }
    public double StockAccuracy { get; set; }
    public double OrderFulfillmentRate { get; set; }

    // Monthly movement
    public double MonthlyInboundQty { get; set; }
    public double MonthlyOutboundQty { get; set; }
    public int MonthlyTransactionCount { get; set; }

    // Yearly
    public double YearlyInboundQty { get; set; }
    public double YearlyOutboundQty { get; set; }

    // Counts
    public int PendingInbound { get; set; }
    public int PendingOutbound { get; set; }
    public int ScrapCount { get; set; }
    public int AdjustmentCount { get; set; }
    public int GrCount { get; set; }
    public int DoCount { get; set; }
    public int TransferCount { get; set; }
    public int StockCountCount { get; set; }

    // Collections
    public List<WarehouseKpiDto> WarehouseKpis { get; set; } = new();
    public List<WmsModuleBreakdownDto> ModuleBreakdown { get; set; } = new();
    public List<WmsPipelineSummaryDto> PipelineSummary { get; set; } = new();
    public List<WmsConversionMetricDto> ConversionMetrics { get; set; } = new();
    public List<WmsRecentTransactionDto> RecentTransactions { get; set; } = new();
    public List<WmsActivityFeedDto> ActivityFeeds { get; set; } = new();
    public List<ChartDataPoint> RevenueTrend { get; set; } = new();
    public List<WmsStockMovementPointDto> StockMovementTrend { get; set; } = new();
    public List<WmsYearlyMovementDto> YearlyMovement { get; set; } = new();
    public List<WmsTopProductDto> TopProducts { get; set; } = new();

    // Formatted
    public string FormattedStock => FormatNumber((decimal)TotalStockQty);
    public string FormattedInvValue => FormatNumber(InventoryValue);
    public string FormattedMonthlyIn => FormatNumber((decimal)MonthlyInboundQty);
    public string FormattedMonthlyOut => FormatNumber((decimal)MonthlyOutboundQty);

    private static string FormatNumber(decimal v)
    {
        if (v >= 1_000_000) return (v / 1_000_000m).ToString("F2") + "M";
        if (v >= 1_000) return (v / 1_000m).ToString("F1") + "K";
        return v.ToString("N0");
    }
}

public record GetWmsDashboardQuery() : IRequest<DashboardWmsResponse>;

public class DashboardHandler : IRequestHandler<GetWmsDashboardQuery, DashboardWmsResponse>
{
    private readonly AppDbContext _context;

    public DashboardHandler(AppDbContext context) => _context = context;

    public async Task<DashboardWmsResponse> Handle(GetWmsDashboardQuery request, CancellationToken ct)
    {
        var today = DateTime.Today;
        var monthStart = new DateTime(today.Year, today.Month, 1);
        var yearStart = new DateTime(today.Year, 1, 1);

        // 1. Top 1000 recent confirmed inventory transactions (with product & warehouse)
        var topTxs = await _context.InventoryTransaction
            .AsNoTracking()
            .Where(x => x.ProductId != null && x.WarehouseId != null && x.Status == InventoryTransactionStatus.Confirmed)
            .OrderByDescending(x => x.CreatedAt)
            .Take(1000)
            .Select(x => new
            {
                x.ModuleCode,
                x.ModuleNumber,
                x.ModuleName,
                x.MovementDate,
                x.Movement,
                x.TransType,
                x.Stock,
                x.CreatedAt,
                ProductId = x.ProductId!,
                ProductName = x.Product!.Name,
                ProductNumber = x.Product!.AutoNumber,
                UnitPrice = x.Product!.UnitPrice ?? 0,
                WarehouseId = x.WarehouseId!,
                WarehouseName = x.Warehouse!.Name
            })
            .ToListAsync(ct);

        // Fallback: if table is empty, use count-based metrics from entity tables
        var grCount = await _context.GoodsReceive.AsNoTracking().CountAsync(ct);
        var doCount = await _context.DeliveryOrder.AsNoTracking().CountAsync(ct);
        var scrapCount = await _context.Scrapping.AsNoTracking().CountAsync(ct);
        var adjCount = await _context.PositiveAdjustment.AsNoTracking().CountAsync(ct)
                       + await _context.NegativeAdjustment.AsNoTracking().CountAsync(ct);
        var transferCount = await _context.TransferIn.AsNoTracking().CountAsync(ct);
        var stockCountCount = await _context.StockCount.AsNoTracking().CountAsync(ct);
        var pendingIn = await _context.GoodsReceive.AsNoTracking().CountAsync(x => x.Status == GoodsReceiveStatus.Draft, ct);
        var pendingOut = await _context.DeliveryOrder.AsNoTracking().CountAsync(x => x.Status == DeliveryOrderStatus.Draft, ct);

        // 2. Aggregate from top transactions
        var totalStockQty = topTxs.Sum(x => x.Stock ?? 0);
        var totalSku = topTxs.Select(x => x.ProductId).Distinct().Count();
        var warehouseCount = topTxs.Select(x => x.WarehouseId).Distinct().Count();
        var inventoryValue = topTxs.Sum(x => (decimal)(x.Stock ?? 0) * x.UnitPrice);

        // ModuleCode uses SHORT codes: GR, DO, ADJ+, ADJ-, COUNT, SCRP, TO-IN, TO-OUT, SRN, PRN
        var inboundModules = new HashSet<string> { "GR", "ADJ+", "TO-IN", "PRN" };
        var outboundModules = new HashSet<string> { "DO", "ADJ-", "TO-OUT", "SCRP", "SRN" };

        bool IsInbound(string? mc) => mc != null && inboundModules.Contains(mc);
        bool IsOutbound(string? mc) => mc != null && outboundModules.Contains(mc);

        var monthlyInbound = topTxs
            .Where(x => x.MovementDate >= monthStart && IsInbound(x.ModuleCode))
            .Sum(x => Math.Abs(x.Movement ?? 0));
        var monthlyOutbound = topTxs
            .Where(x => x.MovementDate >= monthStart && IsOutbound(x.ModuleCode))
            .Sum(x => Math.Abs(x.Movement ?? 0));
        var monthlyTxCount = topTxs.Count(x => x.MovementDate >= monthStart);

        var yearlyInbound = topTxs
            .Where(x => x.MovementDate >= yearStart && IsInbound(x.ModuleCode))
            .Sum(x => Math.Abs(x.Movement ?? 0));
        var yearlyOutbound = topTxs
            .Where(x => x.MovementDate >= yearStart && IsOutbound(x.ModuleCode))
            .Sum(x => Math.Abs(x.Movement ?? 0));

        // 3. Stock Accuracy from StockCount transactions (ModuleCode = "COUNT")
        var countTxs = topTxs.Where(x => x.ModuleCode == "COUNT").ToList();
        var stockAccuracy = 100.0;
        if (countTxs.Any())
        {
            var totalSys = countTxs.Sum(x => x.Stock ?? 0);
            var totalDelta = countTxs.Sum(x => Math.Abs((x.Movement ?? 0) - (x.Stock ?? 0)));
            stockAccuracy = totalSys > 0 ? Math.Max(0, Math.Round(100 - (totalDelta / totalSys * 100), 1)) : 100;
        }

        // 4. Order Fulfillment
        var totalDo = await _context.DeliveryOrder.AsNoTracking().CountAsync(x => x.Status != DeliveryOrderStatus.Draft, ct);
        var doConfirmed = await _context.DeliveryOrder.AsNoTracking().CountAsync(x => x.Status == DeliveryOrderStatus.Confirmed, ct);
        var fulfPct = totalDo > 0 ? Math.Round((double)doConfirmed / totalDo * 100, 1) : 0;

        // 5. Warehouse KPIs (group by warehouse from top txs)
        var whColors = new[] { "#2563eb", "#10b981", "#f59e0b", "#8b5cf6", "#ec4899", "#06b6d4" };
        var warehouseKpis = topTxs
            .GroupBy(x => new { x.WarehouseId, x.WarehouseName })
            .Select((g, i) => new WarehouseKpiDto
            {
                WarehouseName = g.Key.WarehouseName ?? "Unknown",
                StockQty = g.Sum(x => x.Stock ?? 0),
                SkuCount = g.Select(x => x.ProductId).Distinct().Count(),
                InventoryValue = g.Sum(x => (decimal)(x.Stock ?? 0) * x.UnitPrice),
                Color = whColors[i % whColors.Length]
            })
            .OrderByDescending(x => x.StockQty)
            .ToList();

        // 6. Module breakdown (by count) — ModuleCode uses SHORT codes
        var moduleColors = new Dictionary<string, string>
        {
            ["GR"] = "#06b6d4", ["DO"] = "#10b981",
            ["ADJ+"] = "#6366f1", ["ADJ-"] = "#ef4444",
            ["COUNT"] = "#f59e0b", ["SCRP"] = "#dc2626",
            ["TO-IN"] = "#8b5cf6", ["TO-OUT"] = "#f43f5e",
            ["PRN"] = "#0891b2", ["SRN"] = "#ea580c"
        };

        var moduleBreakdown = topTxs
            .GroupBy(x => x.ModuleCode ?? "Other")
            .Select(g => new WmsModuleBreakdownDto
            {
                Label = g.Key,
                Count = g.Count(),
                Quantity = g.Sum(x => Math.Abs(x.Movement ?? 0)),
                Color = moduleColors.TryGetValue(g.Key, out var c) ? c : "#94a3b8"
            })
            .OrderByDescending(x => x.Count)
            .ToList();

        if (!moduleBreakdown.Any())
        {
            moduleBreakdown = new List<WmsModuleBreakdownDto>
            {
                new() { Label = "GR", Count = grCount, Color = "#06b6d4" },
                new() { Label = "DO", Count = doCount, Color = "#10b981" },
                new() { Label = "ADJ", Count = adjCount, Color = "#6366f1" },
                new() { Label = "SCRP", Count = scrapCount, Color = "#dc2626" },
                new() { Label = "TO", Count = transferCount, Color = "#8b5cf6" }
            };
        }

        // 7. Pipeline Summary
        var pipelineSummary = new List<WmsPipelineSummaryDto>
        {
            new() { Label = "Total Stock Qty", Value = FormattedSingle((decimal)totalStockQty), IconColorClass = "gc1" },
            new() { Label = "Inventory Value", Value = FormattedSingle(inventoryValue), IconColorClass = "gc2" },
            new() { Label = "Active SKUs", Value = totalSku.ToString("N0"), IconColorClass = "gc3" },
            new() { Label = "Warehouses", Value = warehouseCount.ToString(), IconColorClass = "gc4" }
        };

        // 8. Conversion Metrics
        var conversionMetrics = new List<WmsConversionMetricDto>
        {
            new() { Label = "GR→Stock", Current = grCount, Target = Math.Max(grCount, 1), PctValue = grCount > 0 ? 100 : 0, BarColor = "#06b6d4" },
            new() { Label = "DO Fulfilled", Current = doConfirmed, Target = Math.Max(totalDo, 1), PctValue = fulfPct, BarColor = "#10b981" },
            new() { Label = "Stock Accuracy", Current = stockAccuracy, Target = 100, PctValue = stockAccuracy, BarColor = "#6366f1" },
            new() { Label = "Pending Clear", Current = totalDo - pendingOut, Target = Math.Max(totalDo, 1), PctValue = totalDo > 0 ? (double)(totalDo - pendingOut) / totalDo * 100 : 0, BarColor = "#f59e0b" }
        };

        // 9. Recent Transactions (top 5 from our top-1000 set)
        var recentTransactions = topTxs
            .OrderByDescending(x => x.CreatedAt)
            .Take(5)
            .Select(x => new WmsRecentTransactionDto
            {
                Number = x.ModuleNumber,
                Description = x.ProductName ?? "?",
                Module = x.ModuleCode,
                Quantity = Math.Abs(x.Movement ?? 0),
                Type = x.TransType?.ToString() ?? "-",
                Status = "Confirmed",
                Date = x.CreatedAt
            }).ToList();

        // 10. Activity Feed (from top transactions, most recent 4) — filter to key module types
        var activityModules = new[] { "GR", "DO", "SCRP", "TO-IN", "COUNT" };
        var avatarColors = new[] { "#06b6d4", "#10b981", "#dc2626", "#8b5cf6", "#6366f1" };
        var activityFeeds = topTxs
            .Where(x => x.ModuleCode != null && activityModules.Contains(x.ModuleCode))
            .OrderByDescending(x => x.CreatedAt)
            .Take(4)
            .Select((x, i) =>
            {
                var pn = x.ProductName ?? "?";
                var mc = x.ModuleCode ?? "?";
                var timeAgo = x.CreatedAt.HasValue
                    ? (DateTimeOffset.UtcNow - x.CreatedAt.Value).TotalMinutes < 60
                        ? $"{(int)(DateTimeOffset.UtcNow - x.CreatedAt.Value).TotalMinutes}m ago"
                        : (DateTimeOffset.UtcNow - x.CreatedAt.Value).TotalHours < 24
                            ? $"{(int)(DateTimeOffset.UtcNow - x.CreatedAt.Value).TotalHours}h ago"
                            : $"{(int)(DateTimeOffset.UtcNow - x.CreatedAt.Value).TotalDays}d ago"
                    : "";
                return new WmsActivityFeedDto
                {
                    Title = $"{mc} — {pn}",
                    Detail = $"Qty: {Math.Abs(x.Movement ?? 0):N0} · {x.WarehouseName}",
                    TimeAgo = timeAgo,
                    ChipLabel = mc,
                    ChipColor = i % 2 == 0 ? "chip-cyan" : "chip-amber",
                    AvatarBg = avatarColors[i % avatarColors.Length],
                    AvatarText = mc
                };
            }).ToList();

        // 11. Stock Movement Trend (last 6 months from top transactions)
        var monthNames = new[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
        var last6Months = Enumerable.Range(0, 6)
            .Select(i => today.AddMonths(-5 + i))
            .Select(d => new { d.Year, d.Month })
            .ToList();

        var stockMovementTrend = last6Months.Select(m =>
        {
            var inQty = topTxs
                .Where(x => x.MovementDate.HasValue
                            && x.MovementDate.Value.Year == m.Year
                            && x.MovementDate.Value.Month == m.Month
                            && IsInbound(x.ModuleCode))
                .Sum(x => Math.Abs(x.Movement ?? 0));
            var outQty = topTxs
                .Where(x => x.MovementDate.HasValue
                            && x.MovementDate.Value.Year == m.Year
                            && x.MovementDate.Value.Month == m.Month
                            && IsOutbound(x.ModuleCode))
                .Sum(x => Math.Abs(x.Movement ?? 0));
            return new WmsStockMovementPointDto
            {
                Label = monthNames[m.Month - 1],
                InQty = inQty,
                OutQty = outQty
            };
        }).ToList();

        // 12. SO vs PO revenue trend (from SalesOrder/PurchaseOrder tables, as in CRM)
        var soMonths = await _context.SalesOrder.AsNoTracking()
            .Where(x => x.OrderDate != null)
            .Select(x => x.OrderDate!.Value)
            .ToListAsync(ct);
        var poMonths = await _context.PurchaseOrder.AsNoTracking()
            .Where(x => x.OrderDate != null)
            .Select(x => x.OrderDate!.Value)
            .ToListAsync(ct);
        var allDates = soMonths.Concat(poMonths)
            .Select(d => new { d.Year, d.Month })
            .Distinct()
            .OrderByDescending(x => x.Year).ThenByDescending(x => x.Month)
            .Take(6)
            .Reverse()
            .ToList();

        var revenueTrend = new List<ChartDataPoint>();
        foreach (var ym in allDates)
        {
            var ms = new DateTime(ym.Year, ym.Month, 1);
            var me = ms.AddMonths(1);
            var sa = await _context.SalesOrder.AsNoTracking()
                .Where(x => x.OrderDate >= ms && x.OrderDate < me)
                .SumAsync(x => (double?)x.AfterTaxAmount ?? 0, ct);
            var pa = await _context.PurchaseOrder.AsNoTracking()
                .Where(x => x.OrderDate >= ms && x.OrderDate < me)
                .SumAsync(x => (double?)x.AfterTaxAmount ?? 0, ct);
            revenueTrend.Add(new ChartDataPoint
            {
                Label = monthNames[ym.Month - 1],
                SalesAmount = Math.Round(sa / 1000.0, 1),
                PurchaseAmount = Math.Round(pa / 1000.0, 1)
            });
        }

        // 13. Yearly Movement summary
        var yearlyMovement = new List<WmsYearlyMovementDto>
        {
            new() { Category = "Inbound", Qty = yearlyInbound, Color = "#10b981" },
            new() { Category = "Outbound", Qty = yearlyOutbound, Color = "#e11d48" },
            new() { Category = "Net", Qty = yearlyInbound - yearlyOutbound, Color = "#6366f1" }
        };

        // 14. Top Products by total movement volume
        var topProducts = topTxs
            .GroupBy(x => x.ProductName ?? "?")
            .Select(g => new { Name = g.Key, Total = g.Sum(x => Math.Abs(x.Movement ?? 0)) })
            .OrderByDescending(x => x.Total)
            .Take(5)
            .Select((x, i) => new WmsTopProductDto
            {
                ProductName = x.Name,
                TotalMovement = x.Total,
                Color = whColors[i % whColors.Length]
            })
            .ToList();

        return new DashboardWmsResponse
        {
            TotalStockQty = totalStockQty,
            InventoryValue = inventoryValue,
            TotalSku = totalSku,
            WarehouseCount = warehouseCount,
            StockAccuracy = stockAccuracy,
            OrderFulfillmentRate = fulfPct,
            MonthlyInboundQty = monthlyInbound,
            MonthlyOutboundQty = monthlyOutbound,
            MonthlyTransactionCount = monthlyTxCount,
            YearlyInboundQty = yearlyInbound,
            YearlyOutboundQty = yearlyOutbound,
            PendingInbound = pendingIn,
            PendingOutbound = pendingOut,
            ScrapCount = scrapCount,
            AdjustmentCount = adjCount,
            GrCount = grCount,
            DoCount = doCount,
            TransferCount = transferCount,
            StockCountCount = stockCountCount,
            WarehouseKpis = warehouseKpis,
            ModuleBreakdown = moduleBreakdown,
            PipelineSummary = pipelineSummary,
            ConversionMetrics = conversionMetrics,
            RecentTransactions = recentTransactions,
            ActivityFeeds = activityFeeds,
            RevenueTrend = revenueTrend,
            StockMovementTrend = stockMovementTrend,
            YearlyMovement = yearlyMovement,
            TopProducts = topProducts
        };
    }

    private static string FormattedSingle(decimal v)
    {
        if (v >= 1_000_000) return (v / 1_000_000m).ToString("F2") + "M";
        if (v >= 1_000) return (v / 1_000m).ToString("F1") + "K";
        return v.ToString("N0");
    }
}