import * as React from "react";
import { cn } from "@/lib/utils";
import { formatCurrency } from "@/lib/format-currency";
import { formatDateShort } from "@/lib/format-date";
import {
  STATEMENT_RULE,
  StatementComplianceFooter,
  StatementDocumentHeader,
  StatementField,
  StatementHolderBlock,
  StatementPage,
  StatementPageFooter,
  type StatementComplianceInfo,
  type StatementHolderBusinessFields,
} from "@/components/ui/statement-primitives";
import {
  StatementTransactionTable,
  type StatementTransaction,
} from "@/components/ui/statement-transaction-table";

/**
 * StatementDocument - WealthX DS (L4 Template)
 *
 * The generated bank statement a broker downloads from Reports & Statements.
 * Which fields appear is driven by the account type - the field sets below
 * are transcribed from the `Sample_Open_Banking_statement_<type>.pdf`
 * references on COMPANY-501 and reviewed against them (2026-08-09).
 *
 * Page grammar, shared by every account type:
 *   header → holder | identity fields → balances | detail fields
 *   → linked offset block (mortgage with offset only) → ledger → footer
 *
 * What varies per type is only the right-hand columns: `DETAIL_FIELDS` and
 * the extra balance rows in `BALANCE_EXTRAS`. The ledger is identical for
 * every type - Debit/Credit/Balance with signed, negative balances on loans
 * and cards.
 *
 * Layer: L4 Template
 */

// ---------------------------------------------------------------------------
// Account types & layout mapping
// ---------------------------------------------------------------------------

export type StatementAccountType =
  | "transaction"
  | "savings"
  | "business-transaction"
  | "mortgage"
  | "mortgage-offset"
  | "personal-loan"
  | "credit-card";

export type StatementLayout = "deposit" | "lending" | "credit";

/** Account type → layout family. Add a new account type here and in the field maps below. */
export const ACCOUNT_TYPE_LAYOUT: Record<
  StatementAccountType,
  StatementLayout
> = {
  transaction: "deposit",
  savings: "deposit",
  "business-transaction": "deposit",
  mortgage: "lending",
  "mortgage-offset": "lending",
  "personal-loan": "lending",
  "credit-card": "credit",
};

export const ACCOUNT_TYPE_LABELS: Record<StatementAccountType, string> = {
  transaction: "Transaction account",
  savings: "Savings account",
  "business-transaction": "Business transaction account",
  mortgage: "Mortgage",
  "mortgage-offset": "Mortgage with offset",
  "personal-loan": "Personal loan",
  "credit-card": "Credit card",
};

/** CDR product categories as the samples print them. */
const PRODUCT_CATEGORY: Record<StatementAccountType, string> = {
  transaction: "Transactions/Savings",
  savings: "Transactions/Savings",
  "business-transaction": "Transactions/Savings",
  mortgage: "Residential Mortgage",
  "mortgage-offset": "Residential Mortgage",
  "personal-loan": "Personal Loan",
  "credit-card": "Credit/Charge Card",
};

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

export interface StatementOffsetAccount {
  institutionName?: string;
  accountName?: string;
  bsb?: string;
  accountNo?: string;
  /** CDR product category of the offset account, e.g. "Transactions/Savings". */
  category?: string;
}

export interface StatementAccount {
  accountType: StatementAccountType;
  /** Product name as the institution markets it, e.g. "Complete Home Loan". */
  productName?: string;
  bsb?: string;
  /** Account number, or masked card number for a credit card. */
  accountNo?: string;
  accountStatus?: string;
  institutionName?: string;
  /**
   * Institution logo, sourced from Basiq's institutions API: pass
   * `logo.links.full` from `GET https://au-api.basiq.io/public/institutions`,
   * which serves per-institution SVGs from its own CDN. Some of those files
   * are drawn on a padded square canvas, so the header sizes by height and
   * lets the width run.
   */
  institutionLogo?: string;
  /** ISO date the account was opened. */
  dateOpened?: string;

  /**
   * Balances are signed as the institution reports them: positive funds on a
   * deposit account, negative amounts owing on a loan or card.
   */
  openingBalance?: number;
  closingBalance?: number;
  availableFunds?: number;

  // Deposit
  /** Credit interest rate on a deposit account, as a percentage (e.g. 4.35). */
  interestRate?: number;

  // Lending
  repaymentType?: string;
  /** ISO date the loan started. */
  loanStartDate?: string;
  /** ISO date the loan matures. */
  loanMaturityDate?: string;
  /** Lending rate as a percentage (e.g. 6.14). */
  lendingRate?: number;
  /** e.g. "Variable" or "Fixed". */
  rateType?: string;
  /** ISO date a fixed rate expires. */
  fixedRateExpiry?: string;
  minRepaymentAmount?: number;
  repaymentFrequency?: string;
  availableRedraw?: number;
  offsetAccount?: StatementOffsetAccount;

  // Credit
  creditLimit?: number;
  /** Purchase rate as a percentage (e.g. 20.99). */
  purchaseRate?: number;
  /** Cash advance rate as a percentage (e.g. 21.99). */
  cashAdvanceRate?: number;
}

export interface StatementPagination {
  /** Ledger rows printed on page one, below the summary blocks. */
  firstPageRows: number;
  /** Ledger rows printed on each continuation page. */
  rowsPerPage: number;
}

export interface StatementDocumentProps {
  account: StatementAccount;
  transactions: StatementTransaction[];
  /** ISO date the statement was generated. */
  statementDate: string;
  statementPeriod: { from: string; to: string };
  holder?: {
    name?: string;
    address?: string;
    business?: StatementHolderBusinessFields;
  };
  /**
   * CDR provenance printed at the foot of the last page. `consentProvidedBy`
   * defaults to the holder name.
   */
  compliance?: StatementComplianceInfo;
  /**
   * Split the ledger across multiple A4 pages. Page one carries the summary
   * blocks plus `firstPageRows` rows; continuation pages open with a balance
   * brought forward row; the closing balance and compliance block print on
   * the last page.
   */
  paginate?: StatementPagination;
  /** Tenant logo - the white-label slot on the printed page. */
  companyLogo?: string;
  companyName?: string;
  className?: string;
}

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

const percent = (rate?: number) =>
  rate === undefined ? undefined : `${rate.toFixed(2)}%`;

const money = (value?: number) =>
  value === undefined ? undefined : formatCurrency(value, { decimals: 2 });

const isoDate = (value?: string) =>
  value === undefined ? undefined : formatDateShort(value);

const formatBsb = (bsb?: string) => {
  if (!bsb) return undefined;
  return bsb.length >= 6 ? `${bsb.slice(0, 3)}-${bsb.slice(3)}` : bsb;
};

/** "1 year, 6 months" between the statement date and loan maturity. */
const remainingLoanTerm = (statementDate: string, maturity?: string) => {
  if (!maturity) return undefined;
  const from = new Date(statementDate);
  const to = new Date(maturity);
  const months =
    (to.getFullYear() - from.getFullYear()) * 12 +
    (to.getMonth() - from.getMonth());
  if (months <= 0) return undefined;
  const years = Math.floor(months / 12);
  const rest = months % 12;
  const parts = [
    years > 0 && `${years} ${years === 1 ? "year" : "years"}`,
    rest > 0 && `${rest} ${rest === 1 ? "month" : "months"}`,
  ].filter(Boolean);
  return parts.join(", ");
};

// ---------------------------------------------------------------------------
// Field sets - one array per layout family, transcribed from the samples
// ---------------------------------------------------------------------------

interface FieldContext {
  account: StatementAccount;
  statementDate: string;
}

interface StatementFieldSpec {
  label: string;
  value: (ctx: FieldContext) => React.ReactNode;
}

const DEPOSIT_DETAIL_FIELDS: StatementFieldSpec[] = [
  { label: "Interest rate", value: ({ account }) => percent(account.interestRate) },
];

const LOAN_DETAIL_FIELDS: StatementFieldSpec[] = [
  { label: "Repayment type", value: ({ account }) => account.repaymentType },
  { label: "Loan start date", value: ({ account }) => isoDate(account.loanStartDate) },
  { label: "Loan end date", value: ({ account }) => isoDate(account.loanMaturityDate) },
  {
    label: "Remaining loan term",
    value: ({ account, statementDate }) =>
      remainingLoanTerm(statementDate, account.loanMaturityDate),
  },
  { label: "Interest rate", value: ({ account }) => percent(account.lendingRate) },
  { label: "Rate type", value: ({ account }) => account.rateType },
  { label: "Fixed rate expiry", value: ({ account }) => isoDate(account.fixedRateExpiry) },
  { label: "Minimum repayment", value: ({ account }) => money(account.minRepaymentAmount) },
  { label: "Repayment frequency", value: ({ account }) => account.repaymentFrequency },
];

const CREDIT_DETAIL_FIELDS: StatementFieldSpec[] = [
  { label: "Interest on purchases", value: ({ account }) => percent(account.purchaseRate) },
  { label: "Interest on cash advances", value: ({ account }) => percent(account.cashAdvanceRate) },
  { label: "Minimum repayment", value: ({ account }) => money(account.minRepaymentAmount) },
  { label: "Repayment frequency", value: ({ account }) => account.repaymentFrequency },
];

/**
 * Right-hand detail column per account type. A personal loan prints no detail
 * column in the sample statements - only the balance block.
 */
const DETAIL_FIELDS: Record<StatementAccountType, StatementFieldSpec[]> = {
  transaction: DEPOSIT_DETAIL_FIELDS,
  savings: DEPOSIT_DETAIL_FIELDS,
  "business-transaction": DEPOSIT_DETAIL_FIELDS,
  mortgage: LOAN_DETAIL_FIELDS,
  "mortgage-offset": LOAN_DETAIL_FIELDS,
  "personal-loan": [],
  "credit-card": CREDIT_DETAIL_FIELDS,
};

/** Rows appended to the shared balance block per account type. */
const BALANCE_EXTRAS: Record<StatementAccountType, StatementFieldSpec[]> = {
  transaction: [],
  savings: [],
  "business-transaction": [],
  mortgage: [
    { label: "Max redraw", value: ({ account }) => money(account.availableRedraw) },
  ],
  "mortgage-offset": [
    { label: "Max redraw", value: ({ account }) => money(account.availableRedraw) },
  ],
  "personal-loan": [],
  "credit-card": [
    { label: "Credit limit", value: ({ account }) => money(account.creditLimit) },
  ],
};

// ---------------------------------------------------------------------------
// Sections
// ---------------------------------------------------------------------------

function FieldColumn({
  fields,
  ctx,
}: {
  fields: StatementFieldSpec[];
  ctx: FieldContext;
}) {
  return (
    <div className="flex min-w-0 flex-col gap-2">
      {fields.map((field) => (
        <StatementField
          key={field.label}
          label={field.label}
          value={field.value(ctx)}
        />
      ))}
    </div>
  );
}

function OffsetAccountBlock({ offset }: { offset: StatementOffsetAccount }) {
  const cells: Array<{ label: string; value?: React.ReactNode }> = [
    { label: "Financial institution", value: offset.institutionName },
    { label: "Account BSB", value: formatBsb(offset.bsb) },
    { label: "Account category", value: offset.category },
    { label: "Account name", value: offset.accountName },
    { label: "Account number", value: offset.accountNo },
  ];

  return (
    <div className="grid grid-cols-1 gap-6 md:grid-cols-[1fr_2fr]">
      <span className="text-label-small">Offset account</span>
      <div className="grid grid-cols-1 gap-x-6 gap-y-3 md:grid-cols-3">
        {cells.map((cell) => (
          <div key={cell.label} className="flex flex-col gap-1">
            <span className="text-label-small">{cell.label}</span>
            <span className="text-body-small">{cell.value ?? "Not available"}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// ---------------------------------------------------------------------------
// StatementDocument
// ---------------------------------------------------------------------------

export function StatementDocument({
  account,
  transactions,
  statementDate,
  statementPeriod,
  holder,
  compliance,
  paginate,
  companyLogo,
  companyName,
  className,
}: StatementDocumentProps) {
  const isCredit = account.accountType === "credit-card";
  const isLending = ACCOUNT_TYPE_LAYOUT[account.accountType] === "lending";
  const ctx: FieldContext = { account, statementDate };

  const period = `${formatDateShort(statementPeriod.from)} - ${formatDateShort(
    statementPeriod.to,
  )}`;

  const totalCredits = transactions
    .filter((t) => t.amount > 0)
    .reduce((sum, t) => sum + t.amount, 0);
  const totalDebits = transactions
    .filter((t) => t.amount < 0)
    .reduce((sum, t) => sum + Math.abs(t.amount), 0);

  const identityFields: Array<{ label: string; value?: React.ReactNode }> = [
    { label: "Product name", value: account.productName },
    ...(isCredit ? [] : [{ label: "Account BSB", value: formatBsb(account.bsb) }]),
    { label: "Account/card number", value: account.accountNo },
    { label: "Account type", value: ACCOUNT_TYPE_LABELS[account.accountType] },
    { label: "Product category", value: PRODUCT_CATEGORY[account.accountType] },
    { label: "Account status", value: account.accountStatus ?? "Open" },
    { label: "Account opened date", value: isoDate(account.dateOpened) },
    { label: "Statement period", value: period },
  ];

  const balanceFields: Array<{
    label: string;
    value?: React.ReactNode;
    emphasis?: boolean;
  }> = [
    { label: "Opening balance", value: money(account.openingBalance) },
    { label: "Total credits", value: money(totalCredits) },
    { label: "Total debits", value: money(totalDebits) },
    { label: "Closing balance", value: money(account.closingBalance), emphasis: true },
    { label: "Available balance", value: money(account.availableFunds) },
    ...BALANCE_EXTRAS[account.accountType].map((field) => ({
      label: field.label,
      value: field.value(ctx),
    })),
  ];

  const detailFields = DETAIL_FIELDS[account.accountType];

  const reference = [holder?.name, account.productName]
    .filter(Boolean)
    .join(" - ");

  const complianceInfo: StatementComplianceInfo | undefined = compliance && {
    consentProvidedBy: holder?.name,
    ...compliance,
  };

  const emptyMessage = isLending
    ? "No repayments or interest charges for this statement period."
    : "No transactions for this statement period.";

  const chunks: StatementTransaction[][] = [];
  if (paginate && transactions.length > paginate.firstPageRows) {
    chunks.push(transactions.slice(0, paginate.firstPageRows));
    for (
      let i = paginate.firstPageRows;
      i < transactions.length;
      i += paginate.rowsPerPage
    ) {
      chunks.push(transactions.slice(i, i + paginate.rowsPerPage));
    }
  } else {
    chunks.push(transactions);
  }
  const pageCount = chunks.length;

  const firstPageSections = (
    <>
      <StatementDocumentHeader
        institutionLogo={account.institutionLogo}
        institutionName={account.institutionName}
        companyLogo={companyLogo}
        companyName={companyName}
      />

      <div
        className={cn(
          "grid grid-cols-1 gap-x-12 gap-y-4 border-t pt-4 md:grid-cols-2",
          STATEMENT_RULE,
        )}
      >
        <StatementHolderBlock
          holderName={holder?.name}
          address={holder?.address}
          business={holder?.business}
        />
        <div className="flex min-w-0 flex-col gap-2">
          {identityFields.map((field) => (
            <StatementField
              key={field.label}
              label={field.label}
              value={field.value}
            />
          ))}
        </div>
      </div>

      <div
        className={cn(
          "grid grid-cols-1 gap-x-12 gap-y-4 border-t pt-4 md:grid-cols-2",
          STATEMENT_RULE,
        )}
      >
        <div className="flex min-w-0 flex-col gap-2">
          {balanceFields.map((field) => (
            <StatementField
              key={field.label}
              label={field.label}
              value={field.value}
              emphasis={field.emphasis}
            />
          ))}
        </div>
        {detailFields.length > 0 && <FieldColumn fields={detailFields} ctx={ctx} />}
      </div>

      {account.offsetAccount && (
        <div className={cn("border-t pt-4", STATEMENT_RULE)}>
          <OffsetAccountBlock offset={account.offsetAccount} />
        </div>
      )}
    </>
  );

  if (pageCount === 1) {
    return (
      <StatementPage className={cn("gap-6", className)}>
        {firstPageSections}

        <div className={cn("border-t pt-4", STATEMENT_RULE)}>
          <StatementTransactionTable
            transactions={transactions}
            openingBalance={account.openingBalance}
            closingBalance={account.closingBalance}
            emptyMessage={emptyMessage}
          />
        </div>

        {complianceInfo && <StatementComplianceFooter {...complianceInfo} />}

        <StatementPageFooter
          reference={reference}
          generatedOn={statementDate}
          className="mt-auto"
        />
      </StatementPage>
    );
  }

  return (
    <div className={cn("flex flex-col gap-6", className)}>
      {chunks.map((chunk, index) => {
        const isFirst = index === 0;
        const isLast = index === pageCount - 1;
        const broughtForward = isFirst
          ? account.openingBalance
          : chunks[index - 1][chunks[index - 1].length - 1]?.balance;

        return (
          <StatementPage key={index} className="gap-6">
            {isFirst && firstPageSections}

            <div className={cn(isFirst && cn("border-t pt-4", STATEMENT_RULE))}>
              <StatementTransactionTable
                transactions={chunk}
                openingBalance={broughtForward}
                openingLabel={
                  isFirst ? "Opening balance" : "Balance brought forward"
                }
                closingBalance={isLast ? account.closingBalance : undefined}
                emptyMessage={emptyMessage}
              />
            </div>

            {isLast && complianceInfo && (
              <StatementComplianceFooter {...complianceInfo} />
            )}

            <StatementPageFooter
              reference={reference}
              generatedOn={statementDate}
              pageLabel={`Page ${index + 1} of ${pageCount}`}
              className="mt-auto"
            />
          </StatementPage>
        );
      })}
    </div>
  );
}
