import { readFileSync, existsSync, writeFileSync, mkdirSync, readdirSync } from 'fs';
import { resolve, dirname } from 'path';
import { Contract, JsonRpcProvider } from 'ethers';
import { runTripleStateReconcile } from './omnl-triple-reconcile';

export interface ProofPointStatus {
  id: string;
  name: string;
  status: 'pass' | 'fail' | 'skip' | 'stale';
  detail: string;
  realTime?: boolean;
}

export interface ReserveCapacitySnapshot {
  schemaVersion: '1.0.0';
  generatedAt: string;
  configRef: string;
  minimumSet: string;
  sla: {
    availabilityTargetPercent: number;
    freshnessMaxAgeSeconds: number;
    agreementToleranceBps: number;
  };
  capacity: {
    reserveUsd: number | null;
    m1Usd: number | null;
    m00Usd: number | null;
    coverageRatioBps: number | null;
    issuanceBlocked: boolean | null;
    issuanceBlockReasons: string[];
    velocityZone: string | null;
  };
  tripleState: {
    aligned: boolean | null;
    breaksCount: number | null;
    generatedAt: string | null;
  };
  onChainGate: {
    address: string | null;
    metricsUpdatedAt: number | null;
    coverageRatioBps: number | null;
    m1ToM00Utilization: number | null;
    issuancePaused: boolean | null;
    rpcReachable: boolean;
  };
  proofQuorum: {
    passed: boolean;
    passCount: number;
    requiredCount: number;
    minimumRequired: number;
    points: Record<string, ProofPointStatus>;
  };
  semiPublic: true;
  redactedFields: string[];
  reproduce: {
    commands: string[];
    configPaths: string[];
  };
}

interface MultiPointProofConfig {
  minimumIndependentProofPoints: number;
  sla99999?: {
    freshness?: { maxAgeSeconds?: number };
    agreement?: { toleranceBps?: number };
    availability?: { targetPercent?: number };
  };
  minimumSets?: Record<
    string,
    { requiredProofPointIds?: string[]; description?: string }
  >;
  proofPoints?: Array<{ id: string; name: string; realTime?: boolean }>;
}

interface GruMetricsFile {
  generatedAt?: string;
  guardrails?: {
    coverageRatioBps?: number;
    coverageOk?: boolean;
    coverageMinBps?: number;
    issuanceBlocked?: boolean;
    issuanceBlockReasons?: string[];
  };
  classical?: { reserveRatioBps?: number };
  gruLayers?: {
    M00_liNotionalUsd?: number;
    M1_cStarUsd?: number;
    monetaryBaseUsd?: number;
  };
  physicalReserve?: { usdValue?: number };
  gruVelocity?: { zone?: string };
  onChainGate?: {
    gateAddress?: string | null;
    envVar?: string;
    metricsForUpdate?: {
      coverageRatioBps?: number;
      m1ToM00Utilization?: number;
      issuancePaused?: boolean;
    };
  };
}

let cachedSnapshot: ReserveCapacitySnapshot | null = null;
let cachedAt = 0;

function projectRoot(): string {
  return (
    process.env.PROXMOX_ROOT?.trim() ||
    process.env.PHOENIX_REPO_ROOT?.trim() ||
    resolve(__dirname, '../../../../../..')
  );
}

function readOptionalJson<T>(rel: string): T | null {
  const p = resolve(projectRoot(), rel);
  if (!existsSync(p)) return null;
  try {
    return JSON.parse(readFileSync(p, 'utf8')) as T;
  } catch {
    return null;
  }
}

function ageSeconds(iso?: string | null): number {
  if (!iso) return Infinity;
  return (Date.now() - new Date(iso).getTime()) / 1000;
}

function loadProofConfig(): MultiPointProofConfig | null {
  return readOptionalJson<MultiPointProofConfig>('config/compliance/gru-multi-point-proof.v1.json');
}

const GATE_ABI = [
  'function metrics() view returns (uint256 coverageRatioBps, uint256 m1ToM00Utilization, uint8 velocityZone, bool issuancePaused, uint256 metricsUpdatedAt)',
];

async function readOnChainGate(): Promise<ReserveCapacitySnapshot['onChainGate']> {
  const cfg = readOptionalJson<{ policyGate138?: { envVar?: string } }>(
    'config/compliance/gru-monetary-metrics.v1.json',
  );
  const envVar = cfg?.policyGate138?.envVar || 'GRU_MONETARY_POLICY_GATE_138';
  const address = process.env[envVar]?.trim() || null;
  const rpc = process.env.RPC_URL_138 || process.env.RPC_URL;
  if (!address || !rpc) {
    return {
      address,
      metricsUpdatedAt: null,
      coverageRatioBps: null,
      m1ToM00Utilization: null,
      issuancePaused: null,
      rpcReachable: false,
    };
  }
  try {
    const c = new Contract(address, GATE_ABI, new JsonRpcProvider(rpc, 138));
    const [coverageRatioBps, m1ToM00Utilization, , issuancePaused, metricsUpdatedAt] =
      await c.metrics();
    return {
      address,
      metricsUpdatedAt: Number(metricsUpdatedAt),
      coverageRatioBps: Number(coverageRatioBps),
      m1ToM00Utilization: Number(m1ToM00Utilization),
      issuancePaused: Boolean(issuancePaused),
      rpcReachable: true,
    };
  } catch {
    return {
      address,
      metricsUpdatedAt: null,
      coverageRatioBps: null,
      m1ToM00Utilization: null,
      issuancePaused: null,
      rpcReachable: false,
    };
  }
}

function evaluateProofPoints(
  cfg: MultiPointProofConfig,
  minimumSetKey: string,
  metrics: GruMetricsFile | null,
  triple: Awaited<ReturnType<typeof runTripleStateReconcile>> | null,
  maxAgeSeconds: number,
): ReserveCapacitySnapshot['proofQuorum'] {
  const minSet = cfg.minimumSets?.[minimumSetKey];
  const required = minSet?.requiredProofPointIds || ['P3', 'P4', 'P6'];
  const nameById = new Map((cfg.proofPoints || []).map((p) => [p.id, p.name]));
  const rtById = new Map((cfg.proofPoints || []).map((p) => [p.id, p.realTime]));
  const points: Record<string, ProofPointStatus> = {};
  let passCount = 0;

  for (const id of required) {
    const point: ProofPointStatus = {
      id,
      name: nameById.get(id) || id,
      status: 'skip',
      detail: '',
      realTime: rtById.get(id),
    };

    switch (id) {
      case 'P3':
      case 'P6': {
        if (!metrics) {
          point.detail = 'gru-monetary-metrics-latest.json missing';
          break;
        }
        const age = ageSeconds(metrics.generatedAt);
        const g = metrics.guardrails || {};
        const coverage = g.coverageRatioBps ?? metrics.classical?.reserveRatioBps;
        const blocked = g.issuanceBlocked === true;
        const coverageOk =
          g.coverageOk === true ||
          (coverage !== undefined && Number(coverage) >= Number(g.coverageMinBps ?? 12000));
        const fresh = age <= maxAgeSeconds;
        point.status = !fresh ? 'stale' : coverageOk && !blocked ? 'pass' : 'fail';
        point.detail = `age=${Math.round(age)}s coverageBps=${coverage} blocked=${blocked}`;
        break;
      }
      case 'P4': {
        if (!triple) {
          point.detail = 'triple-state reconcile unavailable';
          break;
        }
        point.status = triple.aligned ? 'pass' : 'fail';
        point.detail = `breaks=${triple.breaks.length} aligned=${triple.aligned}`;
        break;
      }
      case 'P1': {
        const anchor = existsSync(
          resolve(projectRoot(), 'reports/status/vermil-ucc-20242014504/HASH_NOTARIZATION_ANCHOR.txt'),
        );
        point.status = anchor ? 'pass' : 'skip';
        point.detail = anchor ? 'UCC anchor present' : 'missing UCC anchor';
        break;
      }
      case 'P2': {
        const reconDir = resolve(projectRoot(), 'reconciliation');
        let count = 0;
        if (existsSync(reconDir)) {
          count = readdirSync(reconDir).filter((d) => d.startsWith('audit-office')).length;
        }
        point.status = count > 0 ? 'pass' : 'skip';
        point.detail = `audit folders=${count}`;
        break;
      }
      case 'P5': {
        const ms = existsSync(resolve(projectRoot(), 'config/compliance/web3-multisig-deployed.v1.json'));
        point.status = ms ? 'pass' : 'skip';
        point.detail = ms ? 'multisig config present' : 'missing web3-multisig-deployed';
        break;
      }
      default:
        point.detail = 'no evaluator';
    }

    if (point.status === 'pass') passCount += 1;
    points[id] = point;
  }

  const minimumRequired = cfg.minimumIndependentProofPoints || 3;
  return {
    passed: passCount >= minimumRequired,
    passCount,
    requiredCount: required.length,
    minimumRequired,
    points,
  };
}

export async function buildReserveCapacitySnapshot(
  minimumSetKey = 'reserveCapacityLive',
): Promise<ReserveCapacitySnapshot> {
  const proofCfg = loadProofConfig();
  const maxAge = proofCfg?.sla99999?.freshness?.maxAgeSeconds ?? 60;
  const metrics = readOptionalJson<GruMetricsFile>('reports/status/gru-monetary-metrics-latest.json');

  let triple: Awaited<ReturnType<typeof runTripleStateReconcile>> | null = null;
  try {
    triple = await runTripleStateReconcile(process.env.OMNL_HEALTH_LINE_ID?.trim() || undefined);
  } catch {
    triple = null;
  }

  const fileTriple = readOptionalJson<Awaited<ReturnType<typeof runTripleStateReconcile>>>(
    'reports/status/omnl-triple-reconcile-latest.json',
  );
  const fileTripleMaxAgeSec = Number(process.env.GRU_RESERVE_FILE_TRIPLE_MAX_AGE_SEC || 3600);
  const fileTripleFresh =
    fileTriple?.aligned === true && ageSeconds(fileTriple.generatedAt) <= fileTripleMaxAgeSec;
  if ((!triple?.aligned || (triple.breaks?.length ?? 0) > 0) && fileTripleFresh) {
    triple = fileTriple;
  } else if (!triple && fileTriple) {
    triple = fileTriple;
  }

  const onChainGate = await readOnChainGate();
  const g = metrics?.guardrails;

  const snapshot: ReserveCapacitySnapshot = {
    schemaVersion: '1.0.0',
    generatedAt: new Date().toISOString(),
    configRef: 'config/compliance/gru-multi-point-proof.v1.json',
    minimumSet: minimumSetKey,
    sla: {
      availabilityTargetPercent: proofCfg?.sla99999?.availability?.targetPercent ?? 99.999,
      freshnessMaxAgeSeconds: maxAge,
      agreementToleranceBps: proofCfg?.sla99999?.agreement?.toleranceBps ?? 50,
    },
    capacity: {
      reserveUsd: metrics?.physicalReserve?.usdValue ?? metrics?.gruLayers?.monetaryBaseUsd ?? null,
      m1Usd: metrics?.gruLayers?.M1_cStarUsd ?? null,
      m00Usd: metrics?.gruLayers?.M00_liNotionalUsd ?? null,
      coverageRatioBps: g?.coverageRatioBps ?? metrics?.classical?.reserveRatioBps ?? null,
      issuanceBlocked: g?.issuanceBlocked ?? null,
      issuanceBlockReasons: g?.issuanceBlockReasons || [],
      velocityZone: metrics?.gruVelocity?.zone ?? null,
    },
    tripleState: {
      aligned: triple?.aligned ?? null,
      breaksCount: triple?.breaks?.length ?? null,
      generatedAt: triple?.generatedAt ?? null,
    },
    onChainGate,
    proofQuorum: proofCfg
      ? evaluateProofPoints(proofCfg, minimumSetKey, metrics, triple, maxAge)
      : {
          passed: false,
          passCount: 0,
          requiredCount: 3,
          minimumRequired: 3,
          points: {},
        },
    semiPublic: true,
    redactedFields: [
      'fineractGlDetail',
      'custodianStatementPath',
      'privateKeyPaths',
      'fullAuditPackets',
    ],
    reproduce: {
      commands: [
        'pnpm compliance:gru:monetary-metrics',
        'pnpm omnl:reconcile-triple-state',
        'pnpm gru:validate-multi-point-proof',
      ],
      configPaths: [
        'config/compliance/gru-multi-point-proof.v1.json',
        'config/compliance/gru-monetary-metrics.v1.json',
      ],
    },
  };

  return snapshot;
}

export function writeReserveCapacitySnapshot(snapshot: ReserveCapacitySnapshot): string {
  const outRel = 'reports/status/gru-reserve-capacity-latest.json';
  const outPath = resolve(projectRoot(), outRel);
  mkdirSync(dirname(outPath), { recursive: true });
  writeFileSync(outPath, `${JSON.stringify(snapshot, null, 2)}\n`);
  return outPath;
}

export async function refreshReserveCapacityCache(
  minimumSetKey = 'reserveCapacityLive',
): Promise<ReserveCapacitySnapshot> {
  const snapshot = await buildReserveCapacitySnapshot(minimumSetKey);
  try {
    writeReserveCapacitySnapshot(snapshot);
  } catch {
    /* optional when read-only mount */
  }
  cachedSnapshot = snapshot;
  cachedAt = Date.now();
  return snapshot;
}

export function getLatestReserveCapacitySnapshot(): ReserveCapacitySnapshot | null {
  if (cachedSnapshot && Date.now() - cachedAt < 45_000) {
    return cachedSnapshot;
  }
  const fromDisk = readOptionalJson<ReserveCapacitySnapshot>('reports/status/gru-reserve-capacity-latest.json');
  if (fromDisk) {
    cachedSnapshot = fromDisk;
    cachedAt = Date.now();
    return fromDisk;
  }
  return cachedSnapshot;
}

export function buildProofIndex(): Record<string, unknown> {
  const cfg = loadProofConfig();
  const latest = getLatestReserveCapacitySnapshot();
  return {
    generatedAt: new Date().toISOString(),
    configVersion: cfg?.minimumIndependentProofPoints ? '1.0.0' : null,
    minimumSets: cfg?.minimumSets ?? {},
    proofPoints: (cfg?.proofPoints || []).map((p) => ({
      id: p.id,
      name: p.name,
      realTime: p.realTime,
      status: latest?.proofQuorum.points[p.id]?.status ?? 'unknown',
      detail: latest?.proofQuorum.points[p.id]?.detail ?? null,
    })),
    quorum: latest?.proofQuorum ?? null,
    sla: latest?.sla ?? null,
    publicSurfaces: {
      capacity: '/api/v1/reserve/capacity',
      proofIndex: '/api/v1/reserve/proof-index',
      tripleState: '/api/v1/omnl/reconcile/triple-state',
    },
  };
}
