import * as fs from 'fs';
import * as path from 'path';
import { pgGetChartOfAccounts, pgSaveChartOfAccounts } from '@/lib/postgres/data-access';
import type { ChartOfAccount } from '@/lib/types';

type AccountRecord = ChartOfAccount & Record<string, unknown>;

const PRIMARY_SEED_PATH = path.join(process.cwd(), 'seeds', 'chart_of_accounts.seed.json');
const LEGACY_SEED_PATH = path.join(process.cwd(), 'db', 'old-data', 'chart_of_accounts.json');

function readJsonArray(filePath: string): AccountRecord[] {
  try {
    if (!fs.existsSync(filePath)) return [];
    const raw = fs.readFileSync(filePath, 'utf-8');
    const parsed = JSON.parse(raw);
    return Array.isArray(parsed) ? (parsed as AccountRecord[]) : [];
  } catch {
    return [];
  }
}

function normalizeAccount(raw: AccountRecord): AccountRecord | null {
  const id = String(raw?.id ?? '').trim();
  const code = String(raw?.code ?? '').trim();
  const nameAr = String(raw?.nameAr ?? '').trim();
  const nameEn = String(raw?.nameEn ?? '').trim();
  const type = String(raw?.type ?? '').trim();

  if (!id || !code || !nameAr || !nameEn) return null;
  if (type !== 'account' && type !== 'header') return null;

  const parentIdRaw = raw?.parentId;
  const parentId = parentIdRaw === null || parentIdRaw === undefined || String(parentIdRaw).trim() === ''
    ? null
    : String(parentIdRaw).trim();

  const normalized: AccountRecord = {
    ...raw,
    id,
    code,
    nameAr,
    nameEn,
    type: type as 'account' | 'header',
    parentId,
  };

  if (type === 'account') {
    normalized.nature = raw?.nature === 'credit' ? 'credit' : 'debit';
  }

  return normalized;
}

function loadBundledSeedAccounts(): AccountRecord[] {
  const fromPrimary = readJsonArray(PRIMARY_SEED_PATH)
    .map(normalizeAccount)
    .filter((entry): entry is AccountRecord => Boolean(entry));

  if (fromPrimary.length > 0) return fromPrimary;

  return readJsonArray(LEGACY_SEED_PATH)
    .map(normalizeAccount)
    .filter((entry): entry is AccountRecord => Boolean(entry));
}

function mergeAccounts(current: AccountRecord[], seed: AccountRecord[]): { merged: AccountRecord[]; changed: boolean } {
  if (seed.length === 0) return { merged: current, changed: false };

  const merged = current.map((entry) => ({ ...entry }));
  const byId = new Map<string, number>();
  const byCode = new Map<string, number>();

  merged.forEach((entry, index) => {
    const id = String(entry?.id ?? '').trim();
    const code = String(entry?.code ?? '').trim();
    if (id) byId.set(id, index);
    if (code) byCode.set(code, index);
  });

  let changed = false;

  for (const seedEntry of seed) {
    const id = String(seedEntry.id || '').trim();
    const code = String(seedEntry.code || '').trim();

    const existingIndex = byId.get(id) ?? byCode.get(code);

    if (existingIndex === undefined) {
      merged.push({ ...seedEntry });
      const newIndex = merged.length - 1;
      byId.set(id, newIndex);
      byCode.set(code, newIndex);
      changed = true;
      continue;
    }

    const currentEntry = merged[existingIndex];
    const patchedEntry = {
      ...seedEntry,
      ...currentEntry,
      id: String(currentEntry.id || seedEntry.id),
      code: String(currentEntry.code || seedEntry.code),
    };

    const before = JSON.stringify(currentEntry);
    const after = JSON.stringify(patchedEntry);
    if (before !== after) {
      merged[existingIndex] = patchedEntry;
      changed = true;
    }
  }

  return { merged, changed };
}

export async function getChartOfAccountsWithSeedMerge(): Promise<AccountRecord[]> {
  const current = ((await pgGetChartOfAccounts({ page: 1, pageSize: 10000 })).items as AccountRecord[]);
  const seed = loadBundledSeedAccounts();
  const { merged, changed } = mergeAccounts(current, seed);

  if (changed) {
    
    try {
    await pgSaveChartOfAccounts(merged);
    } catch (err: unknown) {
    // Concurrent requests may cause a PostgreSQL deadlock (40P01) or
    // serialization failure (40001). Ignore it — another request already
    // saved the same merged data, so the DB is consistent.
    const code = (err as { code?: string })?.code;
    if (code !== '40P01' && code !== '40001') throw err;
    }
    
  }

  return merged;
}
