require('../config/loadEnv')();

const fs = require('fs');
const path = require('path');
const { spawnSync } = require('child_process');

const projectRoot = path.join(__dirname, '..');

function readText(relativePath) {
  return fs.readFileSync(path.join(projectRoot, relativePath), 'utf8');
}

function fileCheck(key, relativePath) {
  const absolutePath = path.join(projectRoot, relativePath);
  const exists = fs.existsSync(absolutePath) && fs.statSync(absolutePath).size > 0;
  return {
    key,
    ok: exists,
    detail: exists ? relativePath : `${relativePath} is missing or empty.`
  };
}

function parseJsonOutput(output) {
  const value = String(output || '').trim();
  const first = value.indexOf('{');
  const last = value.lastIndexOf('}');
  if (first < 0 || last < first) return null;
  try {
    return JSON.parse(value.slice(first, last + 1));
  } catch (_error) {
    return null;
  }
}

function runMainnetGate() {
  const result = spawnSync(process.execPath, [path.join(__dirname, 'live-mainnet-listing-gate.js')], {
    cwd: projectRoot,
    encoding: 'utf8',
    windowsHide: true,
    maxBuffer: 4 * 1024 * 1024
  });
  const report = parseJsonOutput(result.stdout || result.stderr);
  if (!report) {
    return {
      ok: false,
      status: 'MAINNET_GATE_UNREADABLE',
      technicalPreconditionsMet: false,
      passed: 0,
      total: 0,
      blockers: [{ key: 'live_mainnet_listing_gate', detail: 'Live-mainnet listing gate did not return readable JSON.' }]
    };
  }
  return report;
}

function newestCriticalSourceMtime() {
  const targets = [
    'README.md',
    'server.js',
    'package.json',
    'docs/WHITEPAPER.md',
    'docs/WHITEPAPER_KO.md',
    'docs/TOKEN_DISTRIBUTION_POLICY.md',
    'docs/TOKEN_DISTRIBUTION_POLICY_KO.md',
    'scripts/mainnet-launch-gate.js',
    'scripts/live-mainnet-listing-gate.js',
    'scripts/exchange-integration-readiness.js',
    'scripts/security-preaudit.js',
    'config/exchangeIntegration.example.json'
  ];
  return Math.max(...targets.map((relativePath) => {
    const absolutePath = path.join(projectRoot, relativePath);
    return fs.existsSync(absolutePath) ? fs.statSync(absolutePath).mtimeMs : 0;
  }));
}

function publicSourceReleaseCheck() {
  const manifestPath = path.join(projectRoot, 'public', 'source-release', 'manifest.json');
  if (!fs.existsSync(manifestPath)) {
    return { key: 'current_public_source_release', ok: false, detail: 'Public source manifest is missing.' };
  }
  try {
    const manifestText = fs.readFileSync(manifestPath, 'utf8').replace(/^\uFEFF/, '');
    const manifest = JSON.parse(manifestText);
    const generatedAt = new Date(manifest.generatedAt || 0).getTime();
    const current = Number.isFinite(generatedAt) && generatedAt >= newestCriticalSourceMtime();
    return {
      key: 'current_public_source_release',
      ok: current,
      detail: current
        ? `Public source release is current (${manifest.generatedAt}).`
        : `Public source release (${manifest.generatedAt || 'unknown'}) predates critical project files.`
    };
  } catch (error) {
    return { key: 'current_public_source_release', ok: false, detail: `Public source manifest is invalid: ${error.message}` };
  }
}

function narrativeConsistencyCheck() {
  const readme = readText('README.md');
  const mainnetPolicy = readText('docs/MAINNET_POLICY_DRAFT.md');
  const staleReadme = /mock proof ledger|does not issue a real coin|does not connect to a real blockchain mainnet/i.test(readme);
  const stalePolicy = /possible future PLAI Chain mainnet|currently in internal point, simulation ledger, and controlled testnet preparation phases/i.test(mainnetPolicy);
  const canonicalMainnetDeclared = /canonical PLAIS Besu QBFT mainnet is technically operational/i.test(mainnetPolicy);
  return {
    key: 'public_narrative_consistency',
    ok: !staleReadme && !stalePolicy && canonicalMainnetDeclared,
    detail: !staleReadme && !stalePolicy && canonicalMainnetDeclared
      ? 'README and mainnet policy consistently describe the live canonical network and its locked public controls.'
      : 'Public README/policy does not consistently describe the live canonical network and its locked public controls.'
  };
}

function evidenceDirectoryCheck(key, relativePath, label) {
  const absolutePath = path.join(projectRoot, relativePath);
  const exists = fs.existsSync(absolutePath)
    && fs.statSync(absolutePath).isDirectory()
    && fs.readdirSync(absolutePath).some((name) => !name.startsWith('.'));
  return {
    key,
    ok: exists,
    detail: exists ? `${label} evidence exists.` : `${label} evidence has not been assembled in ${relativePath}.`
  };
}

function readJsonIfPresent(relativePath) {
  const absolutePath = path.join(projectRoot, relativePath);
  if (!fs.existsSync(absolutePath)) return null;
  try {
    return JSON.parse(fs.readFileSync(absolutePath, 'utf8').replace(/^\uFEFF/, ''));
  } catch (_error) {
    return null;
  }
}

function independentSecurityAuditCheck() {
  const attestationPath = 'docs/listing/05-security-audit/external-audit-attestation.json';
  const attestation = readJsonIfPresent(attestationPath);
  const valid = attestation
    && attestation.schemaVersion === 'plais-independent-security-audit-attestation/v1'
    && attestation.independent === true
    && attestation.finalReportAvailable === true
    && String(attestation.auditFirm || '').trim().length >= 2
    && Number.isFinite(new Date(attestation.reportDate || '').getTime())
    && /^[a-f0-9]{64}$/i.test(String(attestation.reportSha256 || ''))
    && /^[a-f0-9]{64}$/i.test(String(attestation.sourceReleaseSha256 || ''))
    && ['PASS', 'PASS_WITH_REMEDIATED_FINDINGS'].includes(attestation.conclusion);
  if (valid) {
    return {
      key: 'independent_security_audit',
      ok: true,
      detail: `Independent audit attestation verified for ${attestation.auditFirm} (${attestation.reportDate}).`
    };
  }
  const internal = readJsonIfPresent('docs/listing/05-security-audit/evidence/internal-preaudit-report.json');
  return {
    key: 'independent_security_audit',
    ok: false,
    detail: internal?.status === 'READY_FOR_EXTERNAL_AUDIT'
      ? `Internal pre-audit is ${internal.status}; independent auditor report and attestation are still required.`
      : `Independent auditor report and a valid ${attestationPath} are required.`
  };
}

function exchangeIntegrationPackCheck() {
  const reportPath = 'docs/listing/06-exchange-integration/evidence/internal-readiness.json';
  const report = readJsonIfPresent(reportPath);
  const requiredFiles = [
    'docs/listing/06-exchange-integration/README_KO.md',
    'docs/listing/06-exchange-integration/NETWORK_SPECIFICATION.md',
    'docs/listing/06-exchange-integration/DEPOSIT_WITHDRAWAL_RUNBOOK.md',
    'docs/listing/06-exchange-integration/EXCHANGE_UAT_CHECKLIST.md',
    'config/exchangeIntegration.example.json'
  ];
  const documentsPresent = requiredFiles.every((relativePath) => {
    const absolutePath = path.join(projectRoot, relativePath);
    return fs.existsSync(absolutePath) && fs.statSync(absolutePath).size > 0;
  });
  const valid = documentsPresent
    && report?.schemaVersion === 'plais-exchange-integration-readiness/v1'
    && report?.status === 'READY_FOR_EXCHANGE_UAT'
    && report?.ok === true
    && report?.exchangeSelected === false
    && report?.exchangeUatComplete === false
    && report?.productionTransactionBroadcast === false
    && report?.writeOperationsPerformed === false
    && Number(report?.passed) === Number(report?.total)
    && Number(report?.total) >= 10;
  return {
    key: 'exchange_integration_pack',
    ok: Boolean(valid),
    detail: valid
      ? 'Exchange-neutral integration pack is READY_FOR_EXCHANGE_UAT; exchange selection and joint UAT remain pending.'
      : `Exchange integration package or read-only readiness evidence is incomplete (${reportPath}).`
  };
}

function main() {
  const mainnetGate = runMainnetGate();
  const automatedChecks = [
    {
      key: 'live_mainnet_listing_gate',
      ok: mainnetGate.technicalPreconditionsMet === true,
      detail: `${mainnetGate.passed || 0}/${mainnetGate.total || 0} live-mainnet technical checks passed; status ${mainnetGate.status || 'unknown'}.`
    },
    fileCheck('whitepaper_en', 'docs/WHITEPAPER.md'),
    fileCheck('whitepaper_ko', 'docs/WHITEPAPER_KO.md'),
    fileCheck('token_distribution_en', 'docs/TOKEN_DISTRIBUTION_POLICY.md'),
    fileCheck('token_distribution_ko', 'docs/TOKEN_DISTRIBUTION_POLICY_KO.md'),
    fileCheck('operator_key_custody_policy', 'docs/OPERATOR_KEY_CUSTODY.md'),
    fileCheck('public_api_hardening_policy', 'docs/PUBLIC_API_HARDENING.md'),
    narrativeConsistencyCheck(),
    publicSourceReleaseCheck(),
    independentSecurityAuditCheck(),
    evidenceDirectoryCheck('legal_and_kyb_pack', 'docs/listing/02-legal-and-governance', 'Legal/KYB'),
    exchangeIntegrationPackCheck()
  ];
  const failedChecks = automatedChecks.filter((row) => !row.ok);
  const manualRequirements = [
    'Issuer/operator corporate documents, beneficial-owner information, and authorized representative evidence',
    'Jurisdiction-specific legal opinion covering token classification, offering history, AML/sanctions, and regulatory compliance',
    'Independent auditor engagement, final report, remediation/retest evidence, report hash, and incident disclosure',
    'Selected exchange private RPC/WS configuration, confirmation/reorg policy, custody setup, joint deposit/withdrawal UAT, and production sign-off',
    'Allocation wallet addresses, audited vesting/timelock controls, TGE circulation amount, and machine-readable unlock schedule',
    'Named technical/security/compliance contacts and 24/7 incident-response process',
    'Project team, governance, ecosystem traction, user/activity metrics, treasury statements, and conflicts disclosures'
  ];
  const report = {
    ok: true,
    schemaVersion: 'plais-exchange-listing-readiness/v1',
    stage: 'LISTING_PREPARATION_01_STARTED',
    automatedReady: failedChecks.length === 0,
    readyForManualReview: failedChecks.length === 0,
    readyToApply: false,
    decision: failedChecks.length === 0
      ? 'AUTOMATED_CHECKS_PASSED_MANUAL_DUE_DILIGENCE_REQUIRED'
      : 'DO_NOT_SUBMIT_REMEDIATE_FIRST',
    automated: {
      passed: automatedChecks.length - failedChecks.length,
      total: automatedChecks.length,
      blockers: failedChecks.map((row) => ({ key: row.key, detail: row.detail })),
      checks: automatedChecks
    },
    mainnetGate: {
      status: mainnetGate.status || 'unknown',
      technicalPreconditionsMet: mainnetGate.technicalPreconditionsMet === true,
      blockers: Array.isArray(mainnetGate.blockers) ? mainnetGate.blockers : []
    },
    manualRequirements,
    checkedAt: new Date().toISOString()
  };
  console.log(JSON.stringify(report, null, 2));
  process.exitCode = report.automatedReady ? 0 : 1;
}

main();
