import { db, eq, and, desc, sql } from "@haushaltsApp/db";
import { debts, debtPayments, transactions } from "@haushaltsApp/db/schema";
import { user } from "@haushaltsApp/db/schema";
import type { CreateDebtInput, CreateDebtPaymentInput } from "@haushaltsApp/shared/schemas/debt.schema";

// ── Types ────────────────────────────────────────────────────────────────────

export type DebtWithProgress = {
  id: string;
  householdId: string;
  userId: string;
  creditorUserId: string | null;
  creditorUserName: string | null;
  label: string;
  creditor: string | null;
  totalAmount: number;
  paidAmount: number;
  remainingAmount: number;
  progressPercent: number;
  notes: string | null;
  createdAt: Date;
  closedAt: Date | null;
};

export type DebtPayment = {
  id: string;
  debtId: string;
  amount: number;
  date: string;
  note: string | null;
  linkedTransactionId: string | null;
  createdAt: Date;
};

// ── Helpers ───────────────────────────────────────────────────────────────────

async function getPaidAmount(debtId: string): Promise<number> {
  const result = await db
    .select({ total: sql<string>`coalesce(sum(${debtPayments.amount}), '0')` })
    .from(debtPayments)
    .where(eq(debtPayments.debtId, debtId));
  return parseFloat(result[0]?.total ?? "0");
}

function withProgress(
  debt: typeof debts.$inferSelect,
  paidAmount: number,
  creditorUserName?: string | null,
): DebtWithProgress {
  const total = parseFloat(debt.totalAmount);
  const remaining = Math.max(0, total - paidAmount);
  const progressPercent = total > 0 ? Math.min(100, (paidAmount / total) * 100) : 0;
  return {
    id: debt.id,
    householdId: debt.householdId,
    userId: debt.userId,
    creditorUserId: debt.creditorUserId,
    creditorUserName: creditorUserName ?? null,
    label: debt.label,
    creditor: debt.creditor,
    totalAmount: total,
    paidAmount,
    remainingAmount: remaining,
    progressPercent,
    notes: debt.notes,
    createdAt: debt.createdAt,
    closedAt: debt.closedAt,
  };
}

// ── Service functions ─────────────────────────────────────────────────────────

export async function getDebts(householdId: string, userId: string): Promise<DebtWithProgress[]> {
  const rows = await db
    .select({ debt: debts, creditorName: user.name })
    .from(debts)
    .leftJoin(user, eq(debts.creditorUserId, user.id))
    .where(and(eq(debts.householdId, householdId), eq(debts.userId, userId)))
    .orderBy(desc(debts.createdAt));

  return Promise.all(
    rows.map(async ({ debt, creditorName }) => {
      const paid = await getPaidAmount(debt.id);
      return withProgress(debt, paid, creditorName);
    }),
  );
}

// Debts where this user is the creditor (= "Forderungen")
export async function getClaims(householdId: string, userId: string): Promise<DebtWithProgress[]> {
  const rows = await db
    .select({ debt: debts, debtorName: user.name })
    .from(debts)
    .leftJoin(user, eq(debts.userId, user.id))
    .where(and(eq(debts.householdId, householdId), eq(debts.creditorUserId, userId)))
    .orderBy(desc(debts.createdAt));

  return Promise.all(
    rows.map(async ({ debt, debtorName }) => {
      const paid = await getPaidAmount(debt.id);
      // creditorUserName = debtor name (for display in claims section)
      return { ...withProgress(debt, paid), creditorUserName: debtorName };
    }),
  );
}

export async function createDebt(
  householdId: string,
  userId: string,
  input: CreateDebtInput,
): Promise<DebtWithProgress> {
  const [debt] = await db
    .insert(debts)
    .values({
      householdId,
      userId,
      creditorUserId: input.creditorUserId ?? null,
      label: input.label,
      creditor: input.creditor ?? null,
      totalAmount: String(input.totalAmount),
      notes: input.notes ?? null,
    })
    .returning();

  // Resolve creditor name if internal
  let creditorUserName: string | null = null;
  if (input.creditorUserId) {
    const [u] = await db.select({ name: user.name }).from(user).where(eq(user.id, input.creditorUserId));
    creditorUserName = u?.name ?? null;
  }

  return withProgress(debt!, 0, creditorUserName);
}

export async function getDebtPayments(debtId: string, householdId: string): Promise<DebtPayment[]> {
  const [debt] = await db
    .select({ id: debts.id })
    .from(debts)
    .where(and(eq(debts.id, debtId), eq(debts.householdId, householdId)));
  if (!debt) return [];

  const rows = await db
    .select()
    .from(debtPayments)
    .where(eq(debtPayments.debtId, debtId))
    .orderBy(desc(debtPayments.createdAt));

  return rows.map((p) => ({
    id: p.id,
    debtId: p.debtId,
    amount: parseFloat(p.amount),
    date: p.date,
    note: p.note,
    linkedTransactionId: p.linkedTransactionId,
    createdAt: p.createdAt,
  }));
}

export async function createDebtPayment(
  householdId: string,
  userId: string,
  input: CreateDebtPaymentInput,
): Promise<{ payment: DebtPayment; debt: DebtWithProgress }> {
  const [debt] = await db
    .select()
    .from(debts)
    .where(
      and(
        eq(debts.id, input.debtId),
        eq(debts.householdId, householdId),
        eq(debts.userId, userId),
      ),
    );
  if (!debt) throw new Error("Debt not found");

  const [tx] = await db
    .insert(transactions)
    .values({
      householdId,
      userId,
      scope: "private",
      type: "expense",
      amount: String(input.amount),
      description: `Rate: ${debt.label}`,
      date: input.date,
    })
    .returning({ id: transactions.id });

  const [payment] = await db
    .insert(debtPayments)
    .values({
      debtId: input.debtId,
      amount: String(input.amount),
      date: input.date,
      note: input.note ?? null,
      linkedTransactionId: tx!.id,
    })
    .returning();

  const paidAmount = await getPaidAmount(input.debtId);
  let updatedDebt = debt;
  if (paidAmount >= parseFloat(debt.totalAmount) && !debt.closedAt) {
    const [closed] = await db
      .update(debts)
      .set({ closedAt: new Date() })
      .where(eq(debts.id, input.debtId))
      .returning();
    updatedDebt = closed ?? debt;
  }

  return {
    payment: {
      id: payment!.id,
      debtId: payment!.debtId,
      amount: parseFloat(payment!.amount),
      date: payment!.date,
      note: payment!.note,
      linkedTransactionId: payment!.linkedTransactionId,
      createdAt: payment!.createdAt,
    },
    debt: withProgress(updatedDebt, paidAmount),
  };
}

export async function deleteDebt(
  id: string,
  householdId: string,
  userId: string,
): Promise<boolean> {
  const result = await db
    .delete(debts)
    .where(and(eq(debts.id, id), eq(debts.householdId, householdId), eq(debts.userId, userId)))
    .returning({ id: debts.id });
  return result.length > 0;
}
