import "server-only";

import { Query } from "node-appwrite";

import {
  DATABASE_ID,
  TABLES,
  type FinanceEntry,
  type Payment,
  type TenantKind,
} from "./schema";
import { createAdminClient } from "./server";
import { toPlain } from "./serialize";

/** Payments this tenant recorded itself. */
export async function listPayments(tenantId: string): Promise<Payment[]> {
  const { tablesDB } = createAdminClient();
  const result = await tablesDB.listRows({
    databaseId: DATABASE_ID,
    tableId: TABLES.payments,
    queries: [
      Query.equal("tenantId", tenantId),
      Query.orderDesc("paymentDate"),
      Query.limit(500),
    ],
  });
  return toPlain(result.rows as unknown as Payment[]);
}

/** Payments the counterpart recorded that involve this tenant. */
export async function listIncomingPayments(tenantId: string): Promise<Payment[]> {
  const { tablesDB } = createAdminClient();
  const result = await tablesDB.listRows({
    databaseId: DATABASE_ID,
    tableId: TABLES.payments,
    queries: [
      Query.equal("counterpartTenantId", tenantId),
      Query.orderDesc("paymentDate"),
      Query.limit(500),
    ],
  });
  return toPlain(result.rows as unknown as Payment[]);
}

export type CounterpartBalance = {
  counterpartTenantId: string;
  currency: string;
  /** sum of receivables (lab) or debts (clinic) from finance_entries */
  invoiced: number;
  /** sum of *confirmed* payments — whoever recorded them */
  paid: number;
  /** invoiced - paid; positive means money is still owed to this tenant */
  open: number;
  /** most recent confirmed payment date if any, useful for sorting */
  lastPaymentAt?: string;
};

/**
 * For a given payment row, figure out whether it represents money flowing
 * *toward* `selfTenantId` from a given counterpart. Same physical payment
 * looks like inflow from one side and outflow from the other — we
 * normalise both shapes into 'who is the counterpart?' here.
 */
function inflowFor(
  p: Payment,
  selfTenantId: string,
  kind: TenantKind,
): { counterpartTenantId: string } | null {
  const inflowDir = kind === "lab" ? "inflow" : "outflow";
  const outflowDir = kind === "lab" ? "outflow" : "inflow";
  if (p.tenantId === selfTenantId && p.direction === inflowDir) {
    return { counterpartTenantId: p.counterpartTenantId };
  }
  if (p.counterpartTenantId === selfTenantId && p.direction === outflowDir) {
    return { counterpartTenantId: p.tenantId };
  }
  return null;
}

export function computeBalancesByCounterpart(args: {
  kind: TenantKind;
  selfTenantId: string;
  entries: FinanceEntry[];
  payments: Payment[];
}): CounterpartBalance[] {
  const isLab = args.kind === "lab";
  const invoiceType = isLab ? "receivable" : "debt";

  const acc = new Map<string, CounterpartBalance>();
  const ensure = (id: string, currency: string): CounterpartBalance => {
    const existing = acc.get(id);
    if (existing) return existing;
    const fresh: CounterpartBalance = {
      counterpartTenantId: id,
      currency,
      invoiced: 0,
      paid: 0,
      open: 0,
    };
    acc.set(id, fresh);
    return fresh;
  };

  for (const e of args.entries) {
    if (e.type !== invoiceType) continue;
    if (!e.counterpartTenantId) continue;
    const row = ensure(e.counterpartTenantId, e.currency ?? "TRY");
    row.invoiced += e.amount;
  }
  for (const p of args.payments) {
    // Only confirmed payments count toward the open balance.
    if (p.status && p.status !== "confirmed") continue;
    const mapped = inflowFor(p, args.selfTenantId, args.kind);
    if (!mapped) continue;
    const row = ensure(mapped.counterpartTenantId, p.currency);
    row.paid += p.amount;
    if (!row.lastPaymentAt || p.paymentDate > row.lastPaymentAt) {
      row.lastPaymentAt = p.paymentDate;
    }
  }
  for (const row of acc.values()) {
    row.open = row.invoiced - row.paid;
  }
  return Array.from(acc.values()).sort((a, b) => b.open - a.open);
}

/**
 * Pending payments that the *other* side recorded and are waiting on this
 * tenant's confirmation. Only applicable on the lab side in our current
 * model (clinics record, labs confirm), but the query is symmetrical.
 */
export function filterPendingForConfirmation(
  payments: Payment[],
  selfTenantId: string,
  kind: TenantKind,
): Payment[] {
  const expectedDirection = kind === "lab" ? "outflow" : "inflow";
  return payments
    .filter(
      (p) =>
        p.status === "pending" &&
        p.counterpartTenantId === selfTenantId &&
        p.direction === expectedDirection,
    )
    .sort((a, b) => (a.paymentDate < b.paymentDate ? 1 : -1));
}
