import "server-only";
import { db } from "./db";

export type Range = { from: Date; to: Date };
const DAY = 86400_000;

export function parseRange(sp: { from?: string; to?: string; preset?: string }): { cur: Range; prev: Range; label: string } {
  const now = new Date();
  const endToday = new Date(now); endToday.setHours(23, 59, 59, 999);
  let from: Date, to: Date = endToday, label: string;
  if (sp.from && sp.to && !isNaN(Date.parse(sp.from)) && !isNaN(Date.parse(sp.to))) {
    from = new Date(sp.from + "T00:00:00"); to = new Date(sp.to + "T23:59:59.999"); label = `${sp.from} to ${sp.to}`;
  } else {
    const days = ({ "7": 7, "30": 30, "90": 90, "365": 365 } as Record<string, number>)[sp.preset ?? "30"] ?? 30;
    from = new Date(endToday.getTime() - days * DAY + 1); from.setHours(0, 0, 0, 0); label = `Last ${days} days`;
  }
  const len = to.getTime() - from.getTime() + 1;
  return { cur: { from, to }, prev: { from: new Date(from.getTime() - len), to: new Date(from.getTime() - 1) }, label };
}

async function periodStats(r: Range, includeDemo: boolean) {
  const demoFilter = includeDemo ? {} : { NOT: { number: { startsWith: "DEMO-" } } };
  const orders = await db.order.findMany({
    where: { createdAt: { gte: r.from, lte: r.to }, ...demoFilter },
    include: { items: true },
  });
  const live = orders.filter((o) => o.fulfillmentStatus !== "CANCELLED");
  const cancelled = orders.filter((o) => o.fulfillmentStatus === "CANCELLED");
  const payments = await db.payment.findMany({
    where: { status: "VERIFIED", verifiedAt: { gte: r.from, lte: r.to }, ...(includeDemo ? {} : { order: { NOT: { number: { startsWith: "DEMO-" } } } }) },
  });
  const sum = (xs: number[]) => xs.reduce((a, b) => a + b, 0);
  const receipts = payments.filter((p) => p.kind !== "REFUND");
  const refunds = payments.filter((p) => p.kind === "REFUND");
  const gross = sum(orders.map((o) => o.total));
  const grossLive = sum(live.map((o) => o.total));
  const refundsOnLive = sum(live.map((o) => o.amountRefunded));
  return {
    orders, live, cancelled,
    grossOrderValue: gross,
    cancelledValue: sum(cancelled.map((o) => o.total)),
    netSales: grossLive - refundsOnLive,
    cashCollected: sum(receipts.map((p) => p.amount)),
    depositsCollected: sum(receipts.filter((p) => p.kind === "DEPOSIT").map((p) => p.amount)),
    balancesCollected: sum(receipts.filter((p) => p.kind === "BALANCE").map((p) => p.amount)),
    fullPayments: sum(receipts.filter((p) => p.kind === "FULL").map((p) => p.amount)),
    codCollected: sum(receipts.filter((p) => p.kind === "COD_COLLECTION").map((p) => p.amount)),
    refunds: sum(refunds.map((p) => p.amount)),
    orderCount: orders.length,
    liveCount: live.length,
    aov: live.length ? Math.round(grossLive / live.length) : 0,
  };
}

export async function dashboardData(range: { cur: Range; prev: Range }, includeDemo: boolean) {
  const now = new Date();
  const [cur, prev] = await Promise.all([periodStats(range.cur, includeDemo), periodStats(range.prev, includeDemo)]);
  const demoWhere = includeDemo ? {} : { NOT: { number: { startsWith: "DEMO-" } } };

  // Outstanding money is a point-in-time figure across all open orders (not range-bound).
  const open = await db.order.findMany({ where: { fulfillmentStatus: { not: "CANCELLED" }, ...demoWhere }, select: { id: true, number: true, customerName: true, total: true, amountPaid: true, paymentPlan: true, balanceDueAt: true, fulfillmentStatus: true, createdAt: true } });
  const owed = open.filter((o) => o.total > o.amountPaid);
  const outstandingPrepaid = owed.filter((o) => o.paymentPlan !== "COD").reduce((s, o) => s + o.total - o.amountPaid, 0);
  const outstandingCod = owed.filter((o) => o.paymentPlan === "COD").reduce((s, o) => s + o.total - o.amountPaid, 0);
  const overdue = owed.filter((o) => o.paymentPlan === "DEPOSIT" && o.balanceDueAt && o.balanceDueAt < now && o.amountPaid > 0);

  // Items sold (non-cancelled orders created in range)
  const byCat = new Map<string, number>(); const byProduct = new Map<string, { name: string; qty: number; revenue: number }>();
  for (const o of cur.live) for (const it of o.items) {
    byCat.set(it.categorySlug, (byCat.get(it.categorySlug) ?? 0) + it.lineTotal);
    const e = byProduct.get(it.productName) ?? { name: it.productName, qty: 0, revenue: 0 };
    e.qty += it.qty; e.revenue += it.lineTotal; byProduct.set(it.productName, e);
  }

  // Daily series: order value and verified cash, per day
  const days: { date: string; orders: number; cash: number }[] = [];
  const dayKey = (d: Date) => d.toISOString().slice(0, 10);
  const idx = new Map<string, number>();
  for (let t = range.cur.from.getTime(); t <= range.cur.to.getTime(); t += 86400_000) { const k = dayKey(new Date(t)); idx.set(k, days.length); days.push({ date: k, orders: 0, cash: 0 }); }
  for (const o of cur.live) { const i = idx.get(dayKey(o.createdAt)); if (i != null) days[i].orders += o.total; }
  const pays = await db.payment.findMany({ where: { status: "VERIFIED", kind: { not: "REFUND" }, verifiedAt: { gte: range.cur.from, lte: range.cur.to }, ...(includeDemo ? {} : { order: demoWhere }) }, select: { amount: true, verifiedAt: true } });
  for (const p of pays) { const i = idx.get(dayKey(p.verifiedAt!)); if (i != null) days[i].cash += p.amount; }

  // Batches
  const batches = await db.preorderBatch.findMany({ include: { product: { select: { name: true, slug: true } } }, orderBy: { closesAt: "asc" } });
  const batchRows = batches.map((b) => ({ id: b.id, productId: b.productId, code: b.code, product: b.product.name, capacity: b.capacity, held: b.heldQty, committed: b.committedQty, opensAt: b.opensAt, closesAt: b.closesAt, dispatchFrom: b.dispatchFrom, dispatchTo: b.dispatchTo, manuallyClosed: b.manuallyClosed }));

  // Repeat customers (by email, all time, non-cancelled)
  const grouped = await db.order.groupBy({ by: ["email"], where: { fulfillmentStatus: { not: "CANCELLED" }, ...demoWhere }, _count: { _all: true } });
  const repeat = grouped.filter((g) => g._count._all > 1).length;

  const demoCount = await db.order.count({ where: { createdAt: { gte: range.cur.from, lte: range.cur.to }, number: { startsWith: "DEMO-" } } });
  const realCount = await db.order.count({ where: { createdAt: { gte: range.cur.from, lte: range.cur.to }, NOT: { number: { startsWith: "DEMO-" } } } });

  return {
    cur, prev, days, byCat: [...byCat].map(([slug, value]) => ({ slug, value })), best: [...byProduct.values()].sort((a, b) => b.qty - a.qty).slice(0, 8),
    outstandingPrepaid, outstandingCod, overdue, batchRows, customers: { total: grouped.length, repeat }, demoCount, realCount,
    pendingVerification: await db.payment.count({ where: { status: "SUBMITTED" } }),
  };
}
