import fs from 'fs';
import path from 'path';

export interface MonetaryUnitSymbolIdentity {
  code: string;
  canonicalSymbol: string;
  wrappedSymbol: string;
  mappingKey: string;
  decimals: number;
}

interface MonetaryUnitManifestEntry {
  code: string;
  canonicalSymbol: string;
  wrappedSymbol: string;
  mappingKey: string;
  decimals: number;
}

interface MonetaryUnitManifestFile {
  monetaryUnits?: MonetaryUnitManifestEntry[];
}

function uniquePaths(paths: Array<string | undefined | null>): string[] {
  const seen = new Set<string>();
  const out: string[] = [];

  for (const candidate of paths) {
    if (typeof candidate !== 'string') continue;
    const trimmed = candidate.trim();
    if (!trimmed || seen.has(trimmed)) continue;
    seen.add(trimmed);
    out.push(trimmed);
  }

  return out;
}

function resolveMonetaryUnitManifestPath(): string | null {
  const candidates = uniquePaths([
    process.env.GRU_MONETARY_UNIT_MANIFEST_PATH,
    process.env.MONETARY_UNIT_MANIFEST_JSON_PATH,
    path.resolve(process.cwd(), 'config/gru-monetary-unit-manifest.json'),
    path.resolve(process.cwd(), '../config/gru-monetary-unit-manifest.json'),
    path.resolve(process.cwd(), '../../config/gru-monetary-unit-manifest.json'),
    path.resolve('/config/gru-monetary-unit-manifest.json'),
    path.resolve(__dirname, '../../../../../config/gru-monetary-unit-manifest.json'),
  ]);

  for (const candidate of candidates) {
    if (fs.existsSync(candidate)) return candidate;
  }

  return null;
}

function loadMonetaryUnitManifest(): MonetaryUnitManifestFile {
  const filePath = resolveMonetaryUnitManifestPath();
  if (!filePath) return {};

  try {
    return JSON.parse(fs.readFileSync(filePath, 'utf8')) as MonetaryUnitManifestFile;
  } catch {
    return {};
  }
}

const monetaryUnitManifest = loadMonetaryUnitManifest();

const MONETARY_UNIT_ENTRIES = (
  Array.isArray(monetaryUnitManifest.monetaryUnits)
    ? monetaryUnitManifest.monetaryUnits
    : []
).map((entry) => ({
  code: String(entry.code || '').trim().toUpperCase(),
  canonicalSymbol: String(entry.canonicalSymbol || '').trim(),
  wrappedSymbol: String(entry.wrappedSymbol || '').trim(),
  mappingKey: String(entry.mappingKey || '').trim(),
  decimals: Number(entry.decimals || 0),
}));

export const MONETARY_UNIT_SUPPORTED = MONETARY_UNIT_ENTRIES.map((entry) => entry.code) as string[];

export const MONETARY_UNIT_BY_CODE: Record<string, MonetaryUnitSymbolIdentity> = Object.fromEntries(
  MONETARY_UNIT_ENTRIES.map((entry) => [
    entry.code,
    {
      code: entry.code,
      canonicalSymbol: entry.canonicalSymbol,
      wrappedSymbol: entry.wrappedSymbol,
      mappingKey: entry.mappingKey,
      decimals: entry.decimals,
    },
  ])
);

export const MONETARY_UNIT_BY_SYMBOL: Record<string, MonetaryUnitSymbolIdentity> = Object.fromEntries(
  MONETARY_UNIT_ENTRIES.flatMap((entry) => [
    [
      entry.canonicalSymbol,
      {
        code: entry.code,
        canonicalSymbol: entry.canonicalSymbol,
        wrappedSymbol: entry.wrappedSymbol,
        mappingKey: entry.mappingKey,
        decimals: entry.decimals,
      },
    ],
    [
      entry.wrappedSymbol,
      {
        code: entry.code,
        canonicalSymbol: entry.canonicalSymbol,
        wrappedSymbol: entry.wrappedSymbol,
        mappingKey: entry.mappingKey,
        decimals: entry.decimals,
      },
    ],
  ])
);

export function isMonetaryUnitSupported(code: string): boolean {
  return Boolean(MONETARY_UNIT_BY_CODE[String(code || '').trim().toUpperCase()]);
}

export function getMonetaryUnitByCode(code: string): MonetaryUnitSymbolIdentity | undefined {
  return MONETARY_UNIT_BY_CODE[String(code || '').trim().toUpperCase()];
}

export function getMonetaryUnitBySymbol(symbol: string): MonetaryUnitSymbolIdentity | undefined {
  return MONETARY_UNIT_BY_SYMBOL[String(symbol || '').trim()];
}
