import { randomUUID } from 'crypto';
import {
  loadIpsasRegistry,
  validateJournalPairWithMatrix,
} from './omnl-ipsas-gl';
import { loadJournalMatrix } from './omnl-journal-matrix';

export interface JournalLine {
  debitGlCode: string;
  creditGlCode: string;
  amount: string;
  currency: string;
  narrative?: string;
}

export interface JournalEntryRecord {
  id: string;
  instructionId?: string;
  status: 'DRAFT' | 'POSTED' | 'REVERSED';
  lines: JournalLine[];
  ipsasRef?: string;
  createdAt: string;
  postedAt?: string;
}

export interface BalanceReservationRecord {
  id: string;
  instructionId: string;
  amount: string;
  currency: string;
  accountRef: string;
  status: 'HELD' | 'RELEASED' | 'CONSUMED';
  createdAt: string;
  releasedAt?: string;
}

const journalStore = new Map<string, JournalEntryRecord>();
const reservationStore = new Map<string, BalanceReservationRecord>();

function loadMatrixSafe() {
  try {
    return loadJournalMatrix();
  } catch {
    return null;
  }
}

export function createJournalEntry(body: {
  instructionId?: string;
  debitGlCode?: string;
  creditGlCode?: string;
  amount?: string;
  currency?: string;
  narrative?: string;
  lines?: JournalLine[];
}): JournalEntryRecord {
  const lines: JournalLine[] =
    body.lines ??
    (body.debitGlCode && body.creditGlCode
      ? [
          {
            debitGlCode: body.debitGlCode,
            creditGlCode: body.creditGlCode,
            amount: String(body.amount ?? '0'),
            currency: String(body.currency ?? 'USD'),
            narrative: body.narrative,
          },
        ]
      : []);

  if (!lines.length) {
    throw new Error('journal entry requires lines[] or debitGlCode/creditGlCode');
  }

  const registry = loadIpsasRegistry();
  const matrix = loadMatrixSafe();
  for (const line of lines) {
    const v = validateJournalPairWithMatrix(registry, matrix, line.debitGlCode, line.creditGlCode);
    if (!v.valid) {
      throw new Error(`IPSAS pair invalid: ${line.debitGlCode}/${line.creditGlCode}`);
    }
  }

  const id = randomUUID();
  const record: JournalEntryRecord = {
    id,
    instructionId: body.instructionId,
    status: 'DRAFT',
    lines,
    ipsasRef: validateJournalPairWithMatrix(registry, matrix, lines[0].debitGlCode, lines[0].creditGlCode).ipsasRef,
    createdAt: new Date().toISOString(),
  };
  journalStore.set(id, record);
  return record;
}

export function postJournalEntry(id: string): JournalEntryRecord {
  const record = journalStore.get(id);
  if (!record) throw new Error('journal entry not found');
  if (record.status === 'POSTED') return record;
  record.status = 'POSTED';
  record.postedAt = new Date().toISOString();
  journalStore.set(id, record);
  return record;
}

export function reverseJournalEntry(id: string): JournalEntryRecord {
  const record = journalStore.get(id);
  if (!record) throw new Error('journal entry not found');
  record.status = 'REVERSED';
  journalStore.set(id, record);
  return record;
}

export function getJournalEntry(id: string): JournalEntryRecord | undefined {
  return journalStore.get(id);
}

export function createBalanceReservation(body: {
  instructionId: string;
  amount: string;
  currency: string;
  accountRef?: string;
  debtorId?: string;
}): BalanceReservationRecord {
  const id = randomUUID();
  const record: BalanceReservationRecord = {
    id,
    instructionId: body.instructionId,
    amount: String(body.amount),
    currency: String(body.currency).toUpperCase(),
    accountRef: body.accountRef ?? body.debtorId ?? 'default',
    status: 'HELD',
    createdAt: new Date().toISOString(),
  };
  reservationStore.set(id, record);
  return record;
}

export function releaseBalanceReservation(id: string): BalanceReservationRecord {
  const record = reservationStore.get(id);
  if (!record) throw new Error('reservation not found');
  record.status = 'RELEASED';
  record.releasedAt = new Date().toISOString();
  reservationStore.set(id, record);
  return record;
}

export function getBalanceReservation(id: string): BalanceReservationRecord | undefined {
  return reservationStore.get(id);
}

/** Test helper */
export function clearPayoutJournalStores(): void {
  journalStore.clear();
  reservationStore.clear();
}
