import { getAppSettings } from "@/lib/app-settings";
import { formatCurrencyAmount, toIntlLocale } from "@/lib/formatters";
import { splitGradeName } from "@/lib/grade-display";
import type { AppLocale } from "@/lib/i18n";
import { getDictionary } from "@/lib/i18n";
import { prisma } from "@/lib/prisma";
import type { DashboardData, LotSnapshot } from "@/types/dashboard";

function formatQty(value: number, locale: AppLocale) {
  return `${new Intl.NumberFormat(toIntlLocale(locale), {
    minimumFractionDigits: 0,
    maximumFractionDigits: 3
  }).format(value)} kg`;
}

function normalizeUnitCode(unitCode: string | null | undefined) {
  return (unitCode ?? "").trim().toLowerCase();
}

function convertQtyToKg(value: number, unitCode: string | null | undefined) {
  const normalizedUnit = normalizeUnitCode(unitCode);
  if (normalizedUnit === "g" || normalizedUnit === "gr" || normalizedUnit === "gram" || normalizedUnit === "grams") {
    return value / 1000;
  }
  return value;
}

function formatCurrency(value: number, locale: AppLocale, currencyCode: string) {
  return formatCurrencyAmount(value, locale, currencyCode, 0);
}

function formatMonthLabel(date: Date, locale: AppLocale) {
  return new Intl.DateTimeFormat(locale === "id" ? "id-ID" : "en-US", {
    month: "short"
  }).format(date);
}

function getAgingDays(receivedAt: Date) {
  const diffMs = Date.now() - receivedAt.getTime();
  return Math.max(0, Math.floor(diffMs / (1000 * 60 * 60 * 24)));
}

export async function getDashboardData(locale: AppLocale): Promise<DashboardData> {
  const dict = getDictionary(locale);
  const settings = await getAppSettings();
  const currencyCode = settings.currency_code;
  const now = new Date();
  const currentMonthStart = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
  const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
  const sixMonthsStart = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - 5, 1));

  const [
    lots,
    recentActivities,
    stockAdjustments,
    purchaseLines,
    regularSaleLines,
    consignmentLines,
    jitSaleLines
  ] = await Promise.all([
    prisma.inventoryLot.findMany({
      where: {
        availableQty: { gt: 0 }
      },
      include: {
        purchase: { select: { agent: { select: { name: true } } } },
        grade: { select: { name: true } },
        warehouse: { select: { name: true } },
        unit: { select: { code: true } }
      },
      orderBy: [{ receivedAt: "desc" }]
    }),
    prisma.auditTrail.findMany({
      take: 5,
      where: {
        summary: { not: null }
      },
      orderBy: [{ occurredAt: "desc" }, { id: "desc" }],
      select: {
        id: true,
        summary: true,
        occurredAt: true
      }
    }),
    prisma.stockAdjustment.findMany({
      where: {
        adjustmentDate: { gte: thirtyDaysAgo },
        adjustmentReason: {
          category: "SHRINKAGE"
        }
      },
      include: {
        adjustmentReason: { select: { category: true } },
        lot: {
          select: {
            unit: {
              select: {
                code: true
              }
            }
          }
        }
      }
    }),
    prisma.purchaseLine.findMany({
      where: {
        purchase: {
          purchaseDate: { gte: sixMonthsStart },
          status: "SUBMITTED"
        }
      },
      select: {
        qtyOrdered: true,
        subtotal: true,
        unit: {
          select: {
            code: true
          }
        },
        purchase: {
          select: { purchaseDate: true }
        }
      }
    }),
    prisma.regularSaleLine.findMany({
      where: {
        regularSale: {
          OR: [{ closeDate: { gte: sixMonthsStart } }, { saleDate: { gte: sixMonthsStart } }]
        }
      },
      select: {
        qtyActualSold: true,
        lot: {
          select: {
            unit: {
              select: {
                code: true
              }
            }
          }
        },
        regularSale: {
          select: {
            saleDate: true,
            closeDate: true
          }
        }
      }
    }),
    prisma.consignmentLine.findMany({
      where: {
        closeDate: { gte: sixMonthsStart }
      },
      select: {
        qtySold: true,
        lot: {
          select: {
            unit: {
              select: {
                code: true
              }
            }
          }
        },
        closeDate: true
      }
    }),
    prisma.jitSaleLine.findMany({
      where: {
        jitSale: {
          OR: [{ closeDate: { gte: sixMonthsStart } }, { saleDate: { gte: sixMonthsStart } }]
        }
      },
      select: {
        qtyActualSold: true,
        jitSale: {
          select: {
            saleDate: true,
            closeDate: true
          }
        }
      }
    })
  ]);

  const activeLots = lots.filter((lot) => lot.status === "ACTIVE");
  const warehouseCount = new Set(lots.map((lot) => lot.warehouse.name)).size;
  const activeStockQty = activeLots.reduce(
    (sum, lot) => sum + convertQtyToKg(lot.availableQty.toNumber(), lot.unit.code),
    0
  );
  const currentMonthPurchaseValue = purchaseLines
    .filter((line) => line.purchase.purchaseDate >= currentMonthStart)
    .reduce((sum, line) => sum + line.subtotal.toNumber(), 0);
  const currentMonthPurchaseQty = purchaseLines
    .filter((line) => line.purchase.purchaseDate >= currentMonthStart)
    .reduce((sum, line) => sum + convertQtyToKg(line.qtyOrdered.toNumber(), line.unit.code), 0);
  const shrinkageQty30Days = stockAdjustments.reduce(
    (sum, adjustment) =>
      sum +
      convertQtyToKg(
        Math.abs(adjustment.qtyChange.toNumber()),
        adjustment.lot.unit.code
      ),
    0
  );
  const totalPurchaseQty = purchaseLines.reduce(
    (sum, line) => sum + convertQtyToKg(line.qtyOrdered.toNumber(), line.unit.code),
    0
  );
  const totalRegularSalesQty = regularSaleLines.reduce(
    (sum, line) =>
      sum + convertQtyToKg(line.qtyActualSold?.toNumber() ?? 0, line.lot.unit.code),
    0
  );
  const totalConsignmentQty = consignmentLines.reduce(
    (sum, line) => sum + convertQtyToKg(line.qtySold.toNumber(), line.lot.unit.code),
    0
  );
  const totalJitSalesQty = jitSaleLines.reduce(
    (sum, line) => sum + (line.qtyActualSold?.toNumber() ?? 0),
    0
  );
  const totalSalesQty = totalRegularSalesQty + totalConsignmentQty + totalJitSalesQty;
  const salesAbsorption = totalPurchaseQty > 0 ? (totalSalesQty / totalPurchaseQty) * 100 : 0;

  const ageBuckets = [
    {
      label: locale === "id" ? "0-30 hari" : "0-30 days",
      count: lots.filter((lot) => {
        const age = getAgingDays(lot.receivedAt);
        return age <= 30;
      }).length,
      tone: "moss" as const
    },
    {
      label: locale === "id" ? "31-60 hari" : "31-60 days",
      count: lots.filter((lot) => {
        const age = getAgingDays(lot.receivedAt);
        return age >= 31 && age <= 60;
      }).length,
      tone: "teal" as const
    },
    {
      label: locale === "id" ? "60+ hari" : "60+ days",
      count: lots.filter((lot) => getAgingDays(lot.receivedAt) > 60).length,
      tone: "danger" as const
    }
  ].map((bucket) => ({
    ...bucket,
    percentage: lots.length > 0 ? Math.round((bucket.count / lots.length) * 100) : 0
  }));

  const criticalLots: LotSnapshot[] = lots
    .map((lot) => {
      const agingDays = getAgingDays(lot.receivedAt);
      const derivedLotStatus: LotSnapshot["lotStatus"] =
        lot.status === "HOLD" ? "HOLD" : agingDays > 45 ? "AGING" : "ACTIVE";
      const severity =
        lot.status === "HOLD" ? 3 : agingDays > 60 ? 2 : lot.availableQty.toNumber() <= 10 ? 1 : 0;
      const attentionStatus: LotSnapshot["attentionStatus"] =
        lot.status === "HOLD"
          ? "HOLD"
          : agingDays > 60
            ? "AGING"
            : "LOW_STOCK";
      const reason =
        attentionStatus === "HOLD"
          ? locale === "id"
            ? "Lot sedang ditahan dan perlu tindak lanjut."
            : "Lot is on hold and requires follow-up."
          : attentionStatus === "AGING"
            ? locale === "id"
              ? `Umur lot ${agingDays} hari, perlu evaluasi rotasi stok.`
              : `Lot age is ${agingDays} days and needs stock rotation review.`
            : locale === "id"
              ? `Stok tersedia tinggal ${formatQty(convertQtyToKg(lot.availableQty.toNumber(), lot.unit.code), locale)}.`
              : `Available stock is only ${formatQty(convertQtyToKg(lot.availableQty.toNumber(), lot.unit.code), locale)}.`;

      return {
        id: lot.id.toString(),
        lotCode: lot.lotCode,
        supplier: lot.purchase?.agent?.name ?? "Pembelian bebas",
        item: splitGradeName(lot.grade?.name).gradeName,
        availableQty: formatQty(convertQtyToKg(lot.availableQty.toNumber(), lot.unit.code), locale),
        lotStatus: derivedLotStatus,
        attentionStatus,
        reason,
        severity,
        agingDays
      };
    })
    .filter((lot) => lot.severity > 0)
    .sort((a, b) => b.severity - a.severity || b.agingDays - a.agingDays)
    .slice(0, 6)
    .map(({ severity, agingDays, ...lot }) => lot);

  const partnerMap = new Map<
    string,
    { lotCount: number; qty: number; inventoryValue: number }
  >();
  lots.forEach((lot) => {
    const name = lot.purchase?.agent?.name ?? "Pembelian bebas";
    const current = partnerMap.get(name) ?? { lotCount: 0, qty: 0, inventoryValue: 0 };
    current.lotCount += 1;
    const availableQtyKg = convertQtyToKg(lot.availableQty.toNumber(), lot.unit.code);
    current.qty += availableQtyKg;
    current.inventoryValue += availableQtyKg * lot.unitCost.toNumber();
    partnerMap.set(name, current);
  });

  const topPartners = Array.from(partnerMap.entries())
    .sort((a, b) => b[1].qty - a[1].qty)
    .slice(0, 3)
    .map(([name, summary]) => ({
      name,
      detail:
        locale === "id"
          ? `${summary.lotCount} lot / ${formatQty(summary.qty, locale)}`
          : `${summary.lotCount} lots / ${formatQty(summary.qty, locale)}`,
      inventoryValue: formatCurrency(summary.inventoryValue, locale, currencyCode)
    }));

  const monthlyMap = new Map<string, { label: string; purchaseQty: number; salesQty: number }>();
  for (let offset = 0; offset < 6; offset += 1) {
    const date = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - (5 - offset), 1));
    const key = `${date.getUTCFullYear()}-${date.getUTCMonth()}`;
    monthlyMap.set(key, {
      label: formatMonthLabel(date, locale),
      purchaseQty: 0,
      salesQty: 0
    });
  }

  purchaseLines.forEach((line) => {
    const date = line.purchase.purchaseDate;
    const key = `${date.getUTCFullYear()}-${date.getUTCMonth()}`;
    const current = monthlyMap.get(key);
    if (current) {
      current.purchaseQty += line.qtyOrdered.toNumber();
    }
  });

  regularSaleLines.forEach((line) => {
    const date = line.regularSale.closeDate ?? line.regularSale.saleDate;
    const key = `${date.getUTCFullYear()}-${date.getUTCMonth()}`;
    const current = monthlyMap.get(key);
    if (current) {
      current.salesQty += line.qtyActualSold?.toNumber() ?? 0;
    }
  });

  consignmentLines.forEach((line) => {
    const date = line.closeDate;
    if (!date) return;
    const key = `${date.getUTCFullYear()}-${date.getUTCMonth()}`;
    const current = monthlyMap.get(key);
    if (current) {
      current.salesQty += line.qtySold.toNumber();
    }
  });

  jitSaleLines.forEach((line) => {
    const date = line.jitSale.closeDate ?? line.jitSale.saleDate;
    const key = `${date.getUTCFullYear()}-${date.getUTCMonth()}`;
    const current = monthlyMap.get(key);
    if (current) {
      current.salesQty += line.qtyActualSold?.toNumber() ?? 0;
    }
  });

  return {
    metrics: [
      {
        label: locale === "id" ? "Stok aktif" : "Active stock",
        value: formatQty(activeStockQty, locale),
        delta:
          locale === "id"
            ? `${activeLots.length} lot aktif`
            : `${activeLots.length} active lots`
      },
      {
        label: locale === "id" ? "Nilai pembelian bulan ini" : "Purchases this month value",
        value: formatCurrency(currentMonthPurchaseValue, locale, currencyCode),
        delta:
          locale === "id"
            ? `${warehouseCount} gudang terisi`
            : `${warehouseCount} active warehouses`
      },
      {
        label: locale === "id" ? "Pembelian bulan ini" : "Purchases this month",
        value: formatQty(currentMonthPurchaseQty, locale),
        delta:
          locale === "id"
            ? `${purchaseLines.filter((line) => line.purchase.purchaseDate >= currentMonthStart).length} baris pembelian bulan ini`
            : `${purchaseLines.filter((line) => line.purchase.purchaseDate >= currentMonthStart).length} purchase lines this month`
      },
      {
        label: "Shrinkage 30 hari",
        value: formatQty(shrinkageQty30Days, locale),
        delta:
          locale === "id"
            ? `${stockAdjustments.length} adjustment shrinkage`
            : `${stockAdjustments.length} shrinkage adjustments`
      }
    ],
    averageReceiptAcceptance: `${new Intl.NumberFormat(locale === "id" ? "id-ID" : "en-US", {
      minimumFractionDigits: 1,
      maximumFractionDigits: 1
    }).format(salesAbsorption)}%`,
    averageReceiptAcceptanceCopy:
      locale === "id"
        ? "Mengukur seberapa besar qty pembelian yang sudah terserap menjadi penjualan dalam 6 bulan terakhir."
        : "Measures how much purchased quantity has been absorbed into sales over the last 6 months.",
    lotAgeDistribution: ageBuckets,
    recentActivity: recentActivities.map((item) => ({
      id: item.id.toString(),
      summary: item.summary ?? item.id.toString(),
      occurredAt: item.occurredAt.toISOString()
    })),
    criticalLots,
    topPartners,
    monthlyVolume: Array.from(monthlyMap.values())
  };
}
