import { db } from "@/db";
import {
  bankAccounts,
  transactions,
  recurringRules,
  categories,
  type BankAccount,
} from "@/db/schema";
import { and, eq, gte, lte, asc } from "drizzle-orm";
import { buildLedger, type LedgerRow } from "@/lib/recurring";

export type LedgerOptions = {
  householdId: string;
  from: Date;
  to: Date;
  bankAccountId?: string;
};

function toISODate(d: Date): string {
  const y = d.getUTCFullYear();
  const m = String(d.getUTCMonth() + 1).padStart(2, "0");
  const day = String(d.getUTCDate()).padStart(2, "0");
  return `${y}-${m}-${day}`;
}

export async function loadLedger(opts: LedgerOptions): Promise<{
  rows: LedgerRow[];
  accounts: BankAccount[];
}> {
  const accts = await db
    .select()
    .from(bankAccounts)
    .where(eq(bankAccounts.householdId, opts.householdId));

  const accountIds = opts.bankAccountId ? [opts.bankAccountId] : accts.map((a) => a.id);

  const fromIso = toISODate(opts.from);
  const toIso = toISODate(opts.to);

  const filterAccount = opts.bankAccountId
    ? eq(transactions.bankAccountId, opts.bankAccountId)
    : undefined;

  const posted = await db
    .select()
    .from(transactions)
    .where(
      and(
        eq(transactions.householdId, opts.householdId),
        gte(transactions.date, fromIso),
        lte(transactions.date, toIso),
        ...(filterAccount ? [filterAccount] : []),
      ),
    )
    .orderBy(asc(transactions.date));

  const rules = await db
    .select()
    .from(recurringRules)
    .where(
      and(
        eq(recurringRules.householdId, opts.householdId),
        eq(recurringRules.isActive, true),
        ...(opts.bankAccountId
          ? [eq(recurringRules.bankAccountId, opts.bankAccountId)]
          : []),
      ),
    );

  const openingByAccount: Record<string, number> = {};
  for (const a of accts) {
    if (!accountIds.includes(a.id)) continue;
    const opening = a.openingBalanceMinor;
    const beforePosted = await db
      .select()
      .from(transactions)
      .where(
        and(
          eq(transactions.householdId, opts.householdId),
          eq(transactions.bankAccountId, a.id),
          lte(transactions.date, fromIso),
        ),
      );
    const sumBefore = beforePosted
      .filter((t) => t.date < fromIso && t.status !== "cancelled")
      .reduce((s, t) => s + t.amountMinor, 0);
    openingByAccount[a.id] = opening + sumBefore;
  }

  const rows = buildLedger(posted, rules, opts.from, opts.to, openingByAccount);
  return { rows, accounts: accts };
}

export async function getCategoriesMap(householdId: string) {
  const rows = await db
    .select()
    .from(categories)
    .where(eq(categories.householdId, householdId));
  return new Map(rows.map((c) => [c.id, c]));
}
