import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
import { gateChecks, requiredAttestations } from './production-launch-gate.js';

const repoRoot = process.cwd();
const scriptPath = path.join(repoRoot, 'scripts', 'production-launch-gate.js');

function isoNow() {
  return new Date().toISOString();
}

function sampleSummaryValue(expectedValue) {
  if (!expectedValue || typeof expectedValue !== 'object' || Array.isArray(expectedValue)) return expectedValue;
  if (Object.prototype.hasOwnProperty.call(expectedValue, 'eq')) return expectedValue.eq;
  if (Object.prototype.hasOwnProperty.call(expectedValue, 'oneOf')) return expectedValue.oneOf?.[0];
  if (Object.prototype.hasOwnProperty.call(expectedValue, 'lte')) return expectedValue.lte;
  if (Object.prototype.hasOwnProperty.call(expectedValue, 'lt')) return Number(expectedValue.lt) - 1;
  if (Object.prototype.hasOwnProperty.call(expectedValue, 'gte')) return expectedValue.gte;
  if (Object.prototype.hasOwnProperty.call(expectedValue, 'gt')) return Number(expectedValue.gt) + 1;
  return expectedValue;
}

function sampleSummary(summarySpec) {
  return Object.fromEntries(Object.entries(summarySpec || {}).map(([key, value]) => [key, sampleSummaryValue(value)]));
}

function createEvidence(tempDir, overrides = {}) {
  const artifactDir = path.join(tempDir, 'launch-artifacts');
  fs.mkdirSync(artifactDir, { recursive: true });

  const checks = gateChecks.map(spec => {
    const artifact = `launch-artifacts/${spec.id}.log`;
    fs.writeFileSync(path.join(tempDir, artifact), `[PASS] ${spec.id}\n`, 'utf8');
    return {
      id: spec.id,
      status: 'pass',
      command: `npm run ${spec.commandIncludes} -- recorded-for-launch-gate`,
      completedAt: isoNow(),
      artifact,
      summary: sampleSummary(spec.summary),
    };
  });

  const attestations = requiredAttestations.map(spec => ({
    id: spec.id,
    status: 'approved',
    approver: 'test-owner',
    approvedAt: isoNow(),
    notes: spec.label,
  }));

  return {
    schemaVersion: 1,
    environment: 'production',
    commit: '52cef9fabcd1234567890abcdef1234567890abc',
    target: {
      apiBaseUrl: 'https://api.gongxue100.com',
      studentH5Url: 'https://www.gongxue100.com',
      tenantAdminH5Url: 'https://admin.gongxue100.com',
      platformAdminH5Url: 'https://console.gongxue100.com',
    },
    checks,
    attestations,
    ...overrides,
  };
}

function runGate(evidence, options = {}) {
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tiku-launch-gate-'));
  const evidencePath = path.join(tempDir, 'evidence.json');
  const finalEvidence = typeof evidence === 'function' ? evidence(tempDir) : evidence;
  fs.writeFileSync(evidencePath, JSON.stringify(finalEvidence, null, 2), 'utf8');

  const result = spawnSync(process.execPath, [scriptPath, '--evidence', evidencePath, '--json', ...(options.args || [])], {
    cwd: repoRoot,
    encoding: 'utf8',
    env: {
      PATH: process.env.PATH || '',
      Path: process.env.Path || '',
      SystemRoot: process.env.SystemRoot || '',
      ComSpec: process.env.ComSpec || '',
      TEMP: process.env.TEMP || os.tmpdir(),
      TMP: process.env.TMP || os.tmpdir(),
    },
  });

  const payload = JSON.parse(result.stdout || '{}');
  fs.rmSync(tempDir, { recursive: true, force: true });
  return { ...result, payload };
}

const safe = runGate(tempDir => createEvidence(tempDir));
assert.equal(safe.status, 0, `complete launch evidence should pass: ${safe.stdout} ${safe.stderr}`);
assert.equal(safe.payload.summary?.blocker, 0, 'complete launch evidence should have no blockers');

const missingArtifact = runGate(tempDir => {
  const evidence = createEvidence(tempDir);
  const item = evidence.checks.find(check => check.id === 'auth.remote-smoke');
  fs.rmSync(path.join(tempDir, item.artifact), { force: true });
  return evidence;
});
assert.notEqual(missingArtifact.status, 0, 'missing artifact should fail launch gate');
assert.ok(
  missingArtifact.payload.checks?.some(item => item.id === 'check.auth.remote-smoke.artifact' && item.status === 'blocker'),
  'missing artifact should be reported as a blocker',
);

const wrongMigrationProfile = runGate(tempDir => {
  const evidence = createEvidence(tempDir);
  const item = evidence.checks.find(check => check.id === 'migration.pb-production-dry-run');
  item.summary.migrationProfile = 'development';
  return evidence;
});
assert.notEqual(wrongMigrationProfile.status, 0, 'development dry-run evidence should fail launch gate');
assert.ok(
  wrongMigrationProfile.payload.checks?.some(item => item.id === 'check.migration.pb-production-dry-run.summary' && item.status === 'blocker'),
  'migration profile mismatch should be reported as a blocker',
);

const slowBenchmark = runGate(tempDir => {
  const evidence = createEvidence(tempDir);
  const item = evidence.checks.find(check => check.id === 'performance.api-real-data-read');
  item.summary.p95Ms = 301;
  return evidence;
});
assert.notEqual(slowBenchmark.status, 0, 'slow API benchmark should fail launch gate');
assert.ok(
  slowBenchmark.payload.checks?.some(item => item.id === 'check.performance.api-real-data-read.summary' && item.status === 'blocker'),
  'slow API benchmark should be reported as a blocker',
);

const missingBusinessSampling = runGate(tempDir => {
  const evidence = createEvidence(tempDir);
  evidence.checks = evidence.checks.filter(item => item.id !== 'migration.pb-import-sample');
  return evidence;
});
assert.notEqual(missingBusinessSampling.status, 0, 'missing import business sampling should fail launch gate');
assert.ok(
  missingBusinessSampling.payload.checks?.some(item => item.id === 'check.migration.pb-import-sample' && item.status === 'blocker'),
  'missing import business sampling should be reported as a blocker',
);

const missingAttestation = runGate(tempDir => {
  const evidence = createEvidence(tempDir);
  evidence.attestations = evidence.attestations.filter(item => item.id !== 'backup.snapshot');
  return evidence;
});
assert.notEqual(missingAttestation.status, 0, 'missing manual attestation should fail launch gate');
assert.ok(
  missingAttestation.payload.checks?.some(item => item.id === 'attestation.backup.snapshot' && item.status === 'blocker'),
  'missing attestation should be reported as a blocker',
);

console.log('[PASS] production launch gate');
