#!/usr/bin/env node
/**
 * Validates that .mcpb extension dependencies are properly configured.
 *
 * Checks:
 * 1. Required deps exist in server/package.json
 * 2. Excluded deps (bundled inline by esbuild) are not leaked
 * 3. build-extension.sh exclude list matches expected bundled packages
 *
 * Run: node server/scripts/validate-extension-deps.js
 * CI: Part of validate:all
 */

import { readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT_DIR = join(__dirname, '../..');
const SERVER_DIR = join(__dirname, '..');

// Deps that SHOULD be in .mcpb (CJS interop safety net)
// These are packages that don't bundle cleanly due to CJS/ESM interop issues
const MCPB_REQUIRED_DEPS = [
  '@modelcontextprotocol/sdk',
  '@opentelemetry/api',
  '@opentelemetry/exporter-trace-otlp-http',
  '@opentelemetry/resources',
  '@opentelemetry/sdk-node',
  '@opentelemetry/sdk-trace-node',
  '@opentelemetry/semantic-conventions',
  'ajv',
  'diff',
  'express',
  'js-yaml',
  'nunjucks',
  'zod',
];

// Deps that should NOT be in .mcpb (bundled inline by esbuild)
// Keep in sync with exclude array in build-extension.sh
const MCPB_EXCLUDED_DEPS = [
  'chokidar', // Bundled inline by esbuild - file watcher
];

function extractBuildScriptExcludeList() {
  const buildScript = readFileSync(join(ROOT_DIR, 'scripts/build-extension.sh'), 'utf-8');

  // Extract exclude array from: const exclude = ['chokidar'];
  const excludeMatch = buildScript.match(/const exclude = \[([^\]]+)\]/);
  if (!excludeMatch) {
    throw new Error('Could not find exclude array in build-extension.sh');
  }

  // Parse the array contents
  const excludeList = excludeMatch[1]
    .split(',')
    .map((s) => s.trim().replace(/['"]/g, ''))
    .filter((s) => s.length > 0);

  return excludeList;
}

function main() {
  const serverPkg = JSON.parse(readFileSync(join(SERVER_DIR, 'package.json'), 'utf-8'));
  const serverDeps = serverPkg.dependencies;

  let hasErrors = false;
  let hasWarnings = false;

  console.log('Validating .mcpb extension dependencies...\n');

  // Check 1: Required deps exist in server/package.json
  console.log('Checking required deps exist in server/package.json:');
  for (const dep of MCPB_REQUIRED_DEPS) {
    if (!serverDeps[dep]) {
      console.error(`  ERROR: ${dep} required for .mcpb but missing from server/package.json`);
      hasErrors = true;
    } else {
      console.log(`  ✓ ${dep}: ${serverDeps[dep]}`);
    }
  }

  // Check 2: Excluded deps should be in package.json (so they're bundled)
  console.log('\nChecking excluded deps are in package.json (for bundling):');
  for (const dep of MCPB_EXCLUDED_DEPS) {
    if (!serverDeps[dep]) {
      console.warn(`  WARNING: ${dep} not in server/package.json - is it still used?`);
      hasWarnings = true;
    } else {
      console.log(`  ✓ ${dep}: ${serverDeps[dep]} (bundled inline, excluded from .mcpb)`);
    }
  }

  // Check 3: build-extension.sh exclude list matches our expected list
  console.log('\nChecking build-extension.sh exclude list:');
  try {
    const buildExcludeList = extractBuildScriptExcludeList();

    // Check all expected excludes are in build script
    for (const dep of MCPB_EXCLUDED_DEPS) {
      if (!buildExcludeList.includes(dep)) {
        console.error(`  ERROR: ${dep} should be in build-extension.sh exclude list`);
        hasErrors = true;
      }
    }

    // Check for unexpected excludes in build script
    for (const dep of buildExcludeList) {
      if (!MCPB_EXCLUDED_DEPS.includes(dep)) {
        console.warn(`  WARNING: ${dep} in build-extension.sh exclude but not in expected list`);
        hasWarnings = true;
      }
    }

    if (!hasErrors && buildExcludeList.length === MCPB_EXCLUDED_DEPS.length) {
      console.log(`  ✓ Exclude list matches: [${buildExcludeList.join(', ')}]`);
    }
  } catch (error) {
    console.error(`  ERROR: ${error.message}`);
    hasErrors = true;
  }

  // Check 4: Warn about new deps that might need consideration
  console.log('\nChecking for new deps that might need review:');
  const allDeps = Object.keys(serverDeps);
  const knownDeps = [...MCPB_REQUIRED_DEPS, ...MCPB_EXCLUDED_DEPS];
  const unknownDeps = allDeps.filter(
    (dep) => !knownDeps.includes(dep) && !dep.startsWith('@types/')
  );

  if (unknownDeps.length > 0) {
    console.warn('  New dependencies found - review if they should be:');
    console.warn('  - Added to MCPB_REQUIRED_DEPS (needs CJS interop at runtime)');
    console.warn('  - Added to MCPB_EXCLUDED_DEPS (bundled inline by esbuild)');
    for (const dep of unknownDeps) {
      console.warn(`  ? ${dep}: ${serverDeps[dep]}`);
    }
    hasWarnings = true;
  } else {
    console.log('  ✓ No unknown dependencies');
  }

  // Summary
  console.log('\n' + '='.repeat(50));
  if (hasErrors) {
    console.error('FAILED: Extension dependency validation has errors');
    process.exit(1);
  } else if (hasWarnings) {
    console.warn('PASSED with warnings: Review the warnings above');
    process.exit(0);
  } else {
    console.log('PASSED: Extension dependencies validated successfully');
    process.exit(0);
  }
}

main();
