import { randomUUID } from "node:crypto";
import { env } from "../../config/env";
import { getPool } from "../db/pool";
import type { TransactionEntity } from "./transactionStore";

export interface LedgerEntryEntity {
  id: string;
  transaction_id: string;
  merchant_id: string;
  entry_type: "gross_income" | "platform_fee" | "merchant_payable";
  amount: number;
  currency: string;
  direction: "credit" | "debit";
  status: "posted" | "voided";
  metadata_json: Record<string, unknown>;
  created_at: string;
}

function nowIso() {
  return new Date().toISOString();
}

function mapLedgerEntry(row: any): LedgerEntryEntity {
  return {
    id: row.id,
    transaction_id: row.transaction_id,
    merchant_id: row.merchant_id,
    entry_type: row.entry_type,
    amount: Number(row.amount),
    currency: row.currency,
    direction: row.direction,
    status: row.status,
    metadata_json: row.metadata_json || {},
    created_at: row.created_at
  };
}

function roundMoney(value: number) {
  return Math.round((value + Number.EPSILON) * 100) / 100;
}

function calculatePlatformFee(amount: number) {
  const feeBps = Math.max(0, env.FINANCE_PLATFORM_FEE_BPS);
  const feeAmount = roundMoney((amount * feeBps) / 10000);
  return {
    fee_bps: feeBps,
    platform_fee_amount: feeAmount,
    merchant_payable_amount: roundMoney(amount - feeAmount)
  };
}

async function upsertLedgerEntry(payload: {
  transaction_id: string;
  merchant_id: string;
  entry_type: LedgerEntryEntity["entry_type"];
  amount: number;
  currency: string;
  direction: LedgerEntryEntity["direction"];
  metadata_json: Record<string, unknown>;
}): Promise<LedgerEntryEntity> {
  const { rows } = await getPool().query(
    `INSERT INTO ledger_entries (
      id,
      transaction_id,
      merchant_id,
      entry_type,
      amount,
      currency,
      direction,
      status,
      metadata_json,
      created_at
    ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)
    ON CONFLICT (transaction_id, entry_type) DO UPDATE
      SET amount = EXCLUDED.amount,
          currency = EXCLUDED.currency,
          direction = EXCLUDED.direction,
          metadata_json = ledger_entries.metadata_json || EXCLUDED.metadata_json
    RETURNING *`,
    [
      randomUUID(),
      payload.transaction_id,
      payload.merchant_id,
      payload.entry_type,
      payload.amount,
      payload.currency,
      payload.direction,
      "posted",
      payload.metadata_json,
      nowIso()
    ]
  );

  return mapLedgerEntry(rows[0]);
}

export async function createPaidLedgerEntries(tx: TransactionEntity): Promise<LedgerEntryEntity[]> {
  const fee = calculatePlatformFee(tx.amount);
  const baseMetadata = {
    source: "fase1_finance_light",
    partner_reference: tx.partner_reference,
    fee_bps: fee.fee_bps
  };

  const gross = await upsertLedgerEntry({
    transaction_id: tx.id,
    merchant_id: tx.merchant_id,
    entry_type: "gross_income",
    amount: tx.amount,
    currency: tx.currency,
    direction: "credit",
    metadata_json: {
      ...baseMetadata,
      note: "gross paid amount"
    }
  });

  const platformFee = await upsertLedgerEntry({
    transaction_id: tx.id,
    merchant_id: tx.merchant_id,
    entry_type: "platform_fee",
    amount: fee.platform_fee_amount,
    currency: tx.currency,
    direction: "debit",
    metadata_json: {
      ...baseMetadata,
      note: "platform fee deducted from merchant payable"
    }
  });

  const merchantPayable = await upsertLedgerEntry({
    transaction_id: tx.id,
    merchant_id: tx.merchant_id,
    entry_type: "merchant_payable",
    amount: fee.merchant_payable_amount,
    currency: tx.currency,
    direction: "credit",
    metadata_json: {
      ...baseMetadata,
      note: "net merchant payable after platform fee"
    }
  });

  return [gross, platformFee, merchantPayable];
}

export async function createPaidLedgerPlaceholder(tx: TransactionEntity): Promise<LedgerEntryEntity> {
  const [gross] = await createPaidLedgerEntries(tx);
  return gross;
}

export async function listLedgerEntries(filter?: {
  transaction_id?: string;
  merchant_id?: string;
  limit?: number;
}): Promise<LedgerEntryEntity[]> {
  const clauses: string[] = [];
  const params: unknown[] = [];
  let i = 1;

  if (filter?.transaction_id) {
    clauses.push(`transaction_id = $${i++}`);
    params.push(filter.transaction_id);
  }

  if (filter?.merchant_id) {
    clauses.push(`merchant_id = $${i++}`);
    params.push(filter.merchant_id);
  }

  const limit = Math.min(Math.max(filter?.limit || 100, 1), 500);
  const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
  const { rows } = await getPool().query(
    `SELECT * FROM ledger_entries ${where} ORDER BY created_at DESC LIMIT ${limit}`,
    params
  );

  return rows.map(mapLedgerEntry);
}

export function toLedgerEntryPayload(entry: LedgerEntryEntity) {
  return { ...entry };
}
