import { existsSync, readFileSync, readdirSync } from 'node:fs';
import { join, resolve } from 'node:path';

const SUPPORT_VALUES = new Set([
  'core-supported',
  'supported-secondary',
  'parked',
  'archived',
  'extracted',
]);

const SURFACE_CLASS_VALUES = new Set([
  'public-foundation',
  'public-delivery',
  'public-operator',
  'internal',
  'experimental',
]);
const PUBLIC_SURFACE_CLASS_VALUES = new Set([
  'public-foundation',
  'public-delivery',
  'public-operator',
]);
const MATURITY_VALUES = new Set(['stable', 'internal', 'experimental']);
const RELEASE_WAVE_VALUES = ['foundation', 'delivery', 'internal', 'experimental'];
const RELEASE_WAVE_VALUE_SET = new Set(RELEASE_WAVE_VALUES);
const RELEASE_WAVE_RANK = new Map(RELEASE_WAVE_VALUES.map((wave, index) => [wave, index]));
const RETIREMENT_STATUS_VALUES = new Set(['retired', 'deprecated']);
const SUPPORT_DESCRIPTIONS = {
  'core-supported': 'part of the product nucleus and expected to track the vNext architecture closely',
  'supported-secondary': 'still available, but not a strategic anchor for the main product story',
  parked: 'intentionally paused and not expected to move with the main delivery cadence',
  archived: 'preserved for history only and not expected to receive new product work',
  extracted: 'moved out of the monorepo reset surface into a separate line',
};
const SURFACE_CLASS_DESCRIPTIONS = {
  'public-foundation': 'stable public package that defines Decantr foundation contracts and shared building blocks',
  'public-delivery': 'stable public delivery package used directly by end users and teams',
  'public-operator': 'stable public operator-facing package for advanced delivery, verification, or agent workflows',
  internal: 'internal package used inside Decantr implementation and not part of the public release promise',
  experimental: 'opt-in package outside the default supported public surface',
};
const MATURITY_DESCRIPTIONS = {
  stable: 'intended to publish under npm `latest`',
  internal: 'not part of the public npm release promise',
  experimental: 'opt-in and not part of the default publish wave',
};

function isPrereleaseVersion(version) {
  return typeof version === 'string' && /^\d+\.\d+\.\d+-.+/.test(version);
}

function toStableTargetVersion(version) {
  if (typeof version !== 'string' || version.trim().length === 0) {
    return null;
  }

  return version.replace(/-.+$/, '');
}

function describeGraduationLane(entry) {
  if (entry.surfaceClass === 'internal' || entry.maturity === 'internal') {
    return 'internal-only';
  }

  if (entry.surfaceClass === 'experimental' || entry.maturity === 'experimental' || entry.publish === false) {
    return 'experimental-hold';
  }

  if (PUBLIC_SURFACE_CLASS_VALUES.has(entry.surfaceClass) && entry.maturity === 'stable') {
    return 'stable-public';
  }

  return 'policy-invalid';
}

function isMeaningfulStringArray(value) {
  return Array.isArray(value) && value.every((entry) => typeof entry === 'string' && entry.trim().length > 0);
}

export function getRepoRoot() {
  return resolve(new URL('..', import.meta.url).pathname);
}

export function getPackageSupportMatrixPath(root = getRepoRoot()) {
  return join(root, 'docs', 'reference', 'package-support-matrix.md');
}

export function loadPackageSurface(root = getRepoRoot()) {
  const path = join(root, 'config', 'package-surface.json');
  return JSON.parse(readFileSync(path, 'utf8'));
}

export function loadPackageRetirements(root = getRepoRoot()) {
  const path = join(root, 'config', 'package-retirements.json');
  return JSON.parse(readFileSync(path, 'utf8'));
}

export function listPublicPackages(root = getRepoRoot()) {
  const packagesDir = join(root, 'packages');
  return readdirSync(packagesDir)
    .map((name) => join(packagesDir, name, 'package.json'))
    .filter((path) => existsSync(path))
    .map((path) => {
      const pkg = JSON.parse(readFileSync(path, 'utf8'));
      return {
        name: pkg.name,
        version: pkg.version,
        description: pkg.description,
        license: pkg.license,
        homepage: pkg.homepage,
        repository: pkg.repository,
        bugs: pkg.bugs,
        author: pkg.author,
        publishConfig: pkg.publishConfig,
        bin: pkg.bin,
        files: pkg.files,
        private: pkg.private === true,
        path: path.replace(`${root}/`, '').replace(/\/package\.json$/, ''),
      };
    })
    .filter((pkg) => !pkg.private);
}

export function sortReleaseEntries(entries) {
  return [...entries].sort((left, right) => {
    const waveRank = (RELEASE_WAVE_RANK.get(left.releaseWave) ?? Number.MAX_SAFE_INTEGER)
      - (RELEASE_WAVE_RANK.get(right.releaseWave) ?? Number.MAX_SAFE_INTEGER);
    if (waveRank !== 0) return waveRank;

    const orderRank = (left.publishOrder ?? Number.MAX_SAFE_INTEGER) - (right.publishOrder ?? Number.MAX_SAFE_INTEGER);
    if (orderRank !== 0) return orderRank;

    return left.name.localeCompare(right.name);
  });
}

export function validatePackageSurface(surface, publicPackages) {
  const findings = [];
  const byName = new Map(surface.packages.map((entry) => [entry.name, entry]));
  const publicByName = new Map(publicPackages.map((entry) => [entry.name, entry]));
  const seenWaveOrders = new Map();

  for (const entry of surface.packages) {
    if (!SUPPORT_VALUES.has(entry.support)) {
      findings.push(`Unsupported support value for ${entry.name}: ${entry.support}`);
    }
    if (!SURFACE_CLASS_VALUES.has(entry.surfaceClass)) {
      findings.push(`Unsupported surfaceClass value for ${entry.name}: ${entry.surfaceClass}`);
    }
    if (!MATURITY_VALUES.has(entry.maturity)) {
      findings.push(`Unsupported maturity value for ${entry.name}: ${entry.maturity}`);
    }
    if (!RELEASE_WAVE_VALUE_SET.has(entry.releaseWave)) {
      findings.push(`Unsupported releaseWave value for ${entry.name}: ${entry.releaseWave}`);
    }
    if (!Number.isInteger(entry.publishOrder) || entry.publishOrder < 1) {
      findings.push(`Package ${entry.name} publishOrder must be a positive integer.`);
    }
    if (RELEASE_WAVE_VALUE_SET.has(entry.releaseWave) && Number.isInteger(entry.publishOrder) && entry.publishOrder > 0) {
      const waveOrderKey = `${entry.releaseWave}:${entry.publishOrder}`;
      if (seenWaveOrders.has(waveOrderKey)) {
        findings.push(`Duplicate publish order ${entry.publishOrder} in release wave ${entry.releaseWave}: ${seenWaveOrders.get(waveOrderKey)} and ${entry.name}`);
      } else {
        seenWaveOrders.set(waveOrderKey, entry.name);
      }
    }
    if (!publicByName.has(entry.name)) {
      findings.push(`Manifest includes ${entry.name} but no matching public package was found.`);
      continue;
    }
    const pkg = publicByName.get(entry.name);
    if (pkg.path !== entry.path) {
      findings.push(`Manifest path mismatch for ${entry.name}: expected ${pkg.path}, found ${entry.path}`);
    }
    if (typeof pkg.license !== 'string' || pkg.license.trim().length === 0) {
      findings.push(`Public package ${entry.name} must declare a non-empty license.`);
    }
    if (typeof pkg.author !== 'string' || pkg.author.trim().length === 0) {
      findings.push(`Public package ${entry.name} must declare a non-empty author.`);
    }
    if (pkg.homepage !== 'https://decantr.ai') {
      findings.push(`Public package ${entry.name} must declare homepage https://decantr.ai.`);
    }
    if (!pkg.bugs || typeof pkg.bugs !== 'object' || pkg.bugs.url !== 'https://github.com/decantr-ai/decantr/issues') {
      findings.push(`Public package ${entry.name} must declare bugs.url https://github.com/decantr-ai/decantr/issues.`);
    }
    if (!pkg.repository || typeof pkg.repository !== 'object') {
      findings.push(`Public package ${entry.name} must declare repository metadata.`);
    } else {
      if (pkg.repository.type !== 'git') {
        findings.push(`Public package ${entry.name} repository.type must be git.`);
      }
      if (pkg.repository.url !== 'git+https://github.com/decantr-ai/decantr.git') {
        findings.push(`Public package ${entry.name} repository.url must point at git+https://github.com/decantr-ai/decantr.git.`);
      }
      if (pkg.repository.directory !== entry.path) {
        findings.push(`Public package ${entry.name} repository.directory must match ${entry.path}.`);
      }
    }
    if (!isMeaningfulStringArray(pkg.files)) {
      findings.push(`Public package ${entry.name} must declare a non-empty files allowlist.`);
    }
    if (entry.publish === true && pkg.publishConfig?.access !== 'public') {
      findings.push(`Publishable package ${entry.name} must declare publishConfig.access="public".`);
    }
    if (pkg.bin && typeof pkg.bin === 'object') {
      for (const [binName, binPath] of Object.entries(pkg.bin)) {
        if (typeof binPath !== 'string' || binPath.trim().length === 0) {
          findings.push(`Package ${entry.name} bin ${binName} must point to a non-empty path.`);
          continue;
        }
        if (binPath.startsWith('./')) {
          findings.push(`Package ${entry.name} bin ${binName} should use normalized relative paths without a leading ./ (${binPath}).`);
        }
      }
      if (!pkg.files?.includes('dist')) {
        findings.push(`Package ${entry.name} exposes a bin but does not include dist in files.`);
      }
    }
    if (entry.maturity === 'stable' && isPrereleaseVersion(pkg.version)) {
      findings.push(`Stable package ${entry.name} must not use a prerelease semver version (${pkg.version}).`);
    }
    if (entry.maturity === 'internal' && isPrereleaseVersion(pkg.version)) {
      findings.push(`Internal package ${entry.name} should use a normal semver version rather than a prerelease (${pkg.version}).`);
    }
    if (entry.publish === true && entry.defaultDistTag !== 'latest' && !(entry.maturity === 'experimental' && entry.defaultDistTag === 'next')) {
      findings.push(`Published package ${entry.name} must default to the expected dist-tag for its maturity.`);
    }
    if (entry.publish !== true && entry.defaultDistTag != null) {
      findings.push(`Non-published package ${entry.name} must not declare a default npm dist-tag.`);
    }
    if (entry.maturity === 'experimental' && entry.publish !== false) {
      findings.push(`Experimental package ${entry.name} should not publish by default unless explicitly promoted to an opt-in next release.`);
    }
    if (entry.surfaceClass === 'experimental' && entry.releaseWave !== 'experimental') {
      findings.push(`Experimental package ${entry.name} must live in the experimental release wave.`);
    }
    if (entry.surfaceClass === 'internal' && entry.releaseWave !== 'internal') {
      findings.push(`Internal package ${entry.name} must live in the internal release wave.`);
    }
    if (PUBLIC_SURFACE_CLASS_VALUES.has(entry.surfaceClass) && !['foundation', 'delivery'].includes(entry.releaseWave)) {
      findings.push(`Public package ${entry.name} must live in the foundation or delivery release wave.`);
    }
    if (entry.surfaceClass === 'internal' && entry.publish !== false) {
      findings.push(`Internal package ${entry.name} must not publish by default.`);
    }
    if (PUBLIC_SURFACE_CLASS_VALUES.has(entry.surfaceClass) && entry.publish !== true) {
      findings.push(`Public package ${entry.name} must publish by default.`);
    }
    if (PUBLIC_SURFACE_CLASS_VALUES.has(entry.surfaceClass) && entry.maturity !== 'stable') {
      findings.push(`Public package ${entry.name} must use stable maturity now that the beta lane has been removed.`);
    }
    if (entry.surfaceClass === 'internal' && entry.maturity !== 'internal') {
      findings.push(`Internal package ${entry.name} must use internal maturity.`);
    }
    if (entry.surfaceClass === 'experimental' && entry.maturity !== 'experimental') {
      findings.push(`Experimental package ${entry.name} must use experimental maturity.`);
    }

    const readiness = entry.releaseReadiness;
    if (!readiness || typeof readiness !== 'object') {
      findings.push(`Package ${entry.name} is missing releaseReadiness metadata.`);
      continue;
    }
    for (const key of ['stableCandidate', 'docsAligned', 'ciCovered', 'productIntegrated']) {
      if (typeof readiness[key] !== 'boolean') {
        findings.push(`Package ${entry.name} releaseReadiness.${key} must be boolean.`);
      }
    }
    if (!isMeaningfulStringArray(readiness.blockers)) {
      findings.push(`Package ${entry.name} releaseReadiness.blockers must be a string array.`);
    }

    if (PUBLIC_SURFACE_CLASS_VALUES.has(entry.surfaceClass)) {
      if (!readiness.stableCandidate) {
        findings.push(`Public stable package ${entry.name} must be marked releaseReadiness.stableCandidate=true.`);
      }
      if (!readiness.docsAligned || !readiness.ciCovered || !readiness.productIntegrated) {
        findings.push(`Public stable package ${entry.name} must have docsAligned, ciCovered, and productIntegrated set to true.`);
      }
      if ((readiness.blockers ?? []).length > 0) {
        findings.push(`Public stable package ${entry.name} must not carry outstanding release blockers.`);
      }
    }

    if ((entry.surfaceClass === 'internal' || entry.surfaceClass === 'experimental') && entry.releaseReadiness?.stableCandidate) {
      findings.push(`Non-public package ${entry.name} must not be marked as a stable candidate.`);
    }
  }

  for (const pkg of publicPackages) {
    if (!byName.has(pkg.name)) {
      findings.push(`Public package ${pkg.name} is missing from config/package-surface.json.`);
    }
  }

  return findings;
}

export function summarizeReleaseReadiness(surface) {
  const summary = {
    stablePackages: [],
    publicPackages: [],
    internalPackages: [],
    experimentalPackages: [],
    releaseWaves: Object.fromEntries(RELEASE_WAVE_VALUES.map((wave) => [wave, []])),
  };

  for (const entry of surface.packages) {
    summary.releaseWaves[entry.releaseWave]?.push(entry.name);

    if (PUBLIC_SURFACE_CLASS_VALUES.has(entry.surfaceClass)) {
      summary.publicPackages.push(entry.name);
    }

    if (entry.maturity === 'stable' && PUBLIC_SURFACE_CLASS_VALUES.has(entry.surfaceClass)) {
      summary.stablePackages.push(entry.name);
    }

    if (entry.surfaceClass === 'internal') {
      summary.internalPackages.push(entry.name);
    }

    if (entry.maturity === 'experimental') {
      summary.experimentalPackages.push(entry.name);
    }
  }

  return summary;
}

function escapeMarkdownCell(value) {
  let escaped = '';
  for (const char of String(value ?? '-')) {
    if (char === '\\') escaped += '\\\\';
    else if (char === '|') escaped += '\\|';
    else if (char === '\n' || char === '\r') escaped += ' ';
    else escaped += char;
  }
  return escaped;
}

export function renderPackageSupportMatrix(surface, retirements) {
  const activePackages = sortReleaseEntries(surface.packages);
  const nucleusPackages = activePackages.filter((entry) => entry.support === 'core-supported' && entry.surfaceClass !== 'experimental');
  const retiredPackages = [...(retirements?.packages ?? [])].sort((left, right) => left.name.localeCompare(right.name));
  const stablePublic = activePackages.filter((entry) => describeGraduationLane(entry) === 'stable-public');
  const internalOnly = activePackages.filter((entry) => describeGraduationLane(entry) === 'internal-only');
  const experimentalHold = activePackages.filter((entry) => describeGraduationLane(entry) === 'experimental-hold');

  const lines = [
    '# Decantr Package Support Matrix',
    '',
    'Generated from `config/package-surface.json` and `config/package-retirements.json`.',
    'Do not edit manually. Run `node scripts/sync-package-support-matrix.mjs` after package-surface changes.',
    '',
    'Release readiness audit: `pnpm audit:release-readiness`',
    'Package surface audit: `pnpm audit:package-surface`',
    'Package permissions audit: `pnpm audit:package-permissions`',
    'Security permissions reference: `docs/reference/security-permissions.md`',
    '',
    'This matrix defines which npm packages are part of the active Decantr vNext product surface on the reset branch.',
    '',
    '## Active Packages',
    '',
    '| Package | Support status | Surface class | Maturity | Release wave | Default npm tag | Publish default | Stable candidate | Blockers | Release lane | Summary |',
    '| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |',
    ...activePackages.map((entry) => (
      `| \`${escapeMarkdownCell(entry.name)}\` | ${escapeMarkdownCell(entry.support)} | \`${escapeMarkdownCell(entry.surfaceClass)}\` | ${escapeMarkdownCell(entry.maturity)} | \`${escapeMarkdownCell(entry.releaseWave)}\` (\`${escapeMarkdownCell(entry.publishOrder)}\`) | \`${escapeMarkdownCell(entry.defaultDistTag ?? '-')}\` | \`${entry.publish === true ? 'true' : 'false'}\` | \`${entry.releaseReadiness?.stableCandidate === true ? 'true' : 'false'}\` | \`${escapeMarkdownCell((entry.releaseReadiness?.blockers ?? []).length)}\` | \`${describeGraduationLane(entry)}\` | ${escapeMarkdownCell(entry.summary)} |`
    )),
    '',
    '## Adapter Capability Matrix',
    '',
    'Adapter support is intentionally separate from npm package support. `@decantr/cli` is the supported delivery package; adapters describe which framework/project targets it can bootstrap, realize, attach to, style, and verify today.',
    '',
    '| Adapter | Target | Bootstrap | Realize | Attach | Styling | Verify | Notes |',
    '| --- | --- | --- | --- | --- | --- | --- | --- |',
    '| `react-vite` | React + Vite | yes | yes | yes | yes | yes | Primary runnable starter and certified first-mile realization adapter. |',
    '| `next-app` | Next.js App Router / Pages Router | yes | yes | yes | yes | yes | Boots App Router greenfield projects and records App/Pages Router attach metadata. |',
    '| `vanilla-vite` | Plain HTML/CSS/JavaScript + Vite | yes | yes | yes | yes | yes | Framework-free runnable starter that proves the contract is not React-shaped. |',
    '| `vue-vite` | Vue 3 + Vite | yes | yes | yes | yes | yes | Boots Vue Router projects and records Vue route/component attach metadata. |',
    '| `sveltekit` | SvelteKit | yes | yes | yes | yes | yes | Boots SvelteKit file-route projects and records route/layout attach metadata. |',
    '| `angular` | Angular standalone | yes | yes | yes | yes | yes | Boots modern standalone Angular projects and records Angular Router attach metadata. |',
    '| `solid-vite` | Solid + Vite | yes | yes | yes | yes | yes | Boots Solid Vite projects and records Solid route/component attach metadata. |',
    '| `generic-web` | Unsupported or unspecified web targets | no | no | yes | contract-only | basic | Fallback adapter for contract-only Decantr adoption without runtime ownership. |',
    '',
    'Unsupported framework targets are still valid Decantr contract targets. They should resolve through `generic-web` unless and until a runnable adapter lands.',
    '',
    '## Interpretation',
    '',
    ...Object.entries(SUPPORT_DESCRIPTIONS).map(([support, description]) => `- \`${support}\` means ${description}.`),
    ...Object.entries(SURFACE_CLASS_DESCRIPTIONS).map(([surfaceClass, description]) => `- \`${surfaceClass}\` means ${description}.`),
    ...Object.entries(MATURITY_DESCRIPTIONS).map(([maturity, description]) => `- \`${maturity}\` means ${description}.`),
    '- `release wave` defines the intended publish order for coordinated npm releases.',
    '- `publish default` reflects whether the package participates in the default publish flow without opt-in overrides.',
    '- `stable candidate` means the package is intended to be eligible for stable graduation once its blockers reach zero.',
    '- `release lane` is the operator-facing bucket for release planning: `stable-public`, `internal-only`, or `experimental-hold`.',
    '',
    '## Surface Snapshot',
    '',
    `- Stable public: ${stablePublic.length}`,
    `- Internal only: ${internalOnly.length}`,
    `- Experimental hold: ${experimentalHold.length}`,
    '',
    '### Stable Public',
    '',
    ...(stablePublic.length > 0
      ? stablePublic.map((entry) => `- \`${entry.name}\` in \`${entry.releaseWave}\` wave`)
      : ['- none']),
    '',
    '### Internal Only',
    '',
    ...(internalOnly.length > 0
      ? internalOnly.map((entry) => `- \`${entry.name}\` stays inside the implementation boundary and outside the default npm publish track`)
      : ['- none']),
    '',
    '### Experimental Hold',
    '',
    ...(experimentalHold.length > 0
      ? experimentalHold.map((entry) => `- \`${entry.name}\` stays outside the default supported public surface`)
      : ['- none']),
    '',
    '## Current Product Nucleus',
    '',
    'The active Decantr product surface is:',
    '',
    ...nucleusPackages.map((entry) => `- \`${entry.name}\``),
    '',
    '## Explicitly Not Part of the Active Product Story',
    '',
    'These lines were removed from the monorepo reset branch and should not be treated as current product surfaces:',
    '',
    ...retiredPackages.map((entry) => `- \`${entry.name}\`${entry.replacement ? ` -> replacement: ${entry.replacement}` : ''}`),
    '',
    'That retirement path is now executable through:',
    '',
    '1. `config/package-retirements.json`',
    '2. `pnpm package:retire:dry-run`',
    '3. `node scripts/deprecate-retired-packages.mjs`',
    '',
    '## Working Rule',
    '',
    'Any future public package change should update all of:',
    '',
    '1. `config/package-surface.json`',
    '2. `config/package-retirements.json` when a line is being removed',
    '3. `node scripts/sync-package-support-matrix.mjs`',
    '4. the relevant package README',
    '5. publish/deprecation workflow behavior',
    '',
    'Public stable packages must keep `releaseReadiness.blockers` empty, while internal or experimental packages must be clearly classified instead of drifting into long-lived public prerelease states.',
    '',
  ];

  return lines.join('\n');
}

export function createReleasePlan(surface, publicPackages, retirements) {
  const publicByName = new Map(publicPackages.map((entry) => [entry.name, entry]));
  const retiredByName = new Map((retirements?.packages ?? []).map((entry) => [entry.name, entry]));
  const packages = [];

  for (const entry of surface.packages) {
    const pkg = publicByName.get(entry.name) ?? null;
    const blockers = entry.releaseReadiness?.blockers ?? [];
    let recommendedAction = 'hold';
    let releaseTag = null;

    if (retiredByName.has(entry.name)) {
      recommendedAction = 'retired';
    } else if (entry.surfaceClass === 'internal' || entry.maturity === 'internal') {
      recommendedAction = 'hold-internal';
    } else if (entry.maturity === 'experimental' || entry.surfaceClass === 'experimental') {
      if (entry.publish === true) {
        recommendedAction = 'publish-next';
        releaseTag = 'next';
      } else {
        recommendedAction = 'hold-experimental';
      }
    } else if (entry.publish === false) {
      recommendedAction = 'hold-experimental';
    } else if (entry.maturity === 'stable') {
      recommendedAction = 'publish-latest';
      releaseTag = 'latest';
    }

    packages.push({
      name: entry.name,
      path: entry.path,
      version: pkg?.version ?? null,
      stableTargetVersion: pkg?.version ? toStableTargetVersion(pkg.version) : null,
      support: entry.support,
      surfaceClass: entry.surfaceClass,
      maturity: entry.maturity,
      publish: entry.publish === true,
      releaseWave: entry.releaseWave,
      publishOrder: entry.publishOrder,
      defaultDistTag: entry.defaultDistTag,
      summary: entry.summary,
      stableCandidate: entry.releaseReadiness?.stableCandidate === true,
      blockers,
      docsAligned: entry.releaseReadiness?.docsAligned === true,
      ciCovered: entry.releaseReadiness?.ciCovered === true,
      productIntegrated: entry.releaseReadiness?.productIntegrated === true,
      recommendedAction,
      releaseTag,
      retirement: retiredByName.get(entry.name) ?? null,
    });
  }

  for (const retirement of retirements?.packages ?? []) {
    if (surface.packages.some((entry) => entry.name === retirement.name)) continue;
    packages.push({
      name: retirement.name,
      path: null,
      version: null,
      stableTargetVersion: null,
      support: 'retired',
      surfaceClass: 'retired',
      maturity: 'retired',
      publish: false,
      releaseWave: 'retired',
      publishOrder: Number.MAX_SAFE_INTEGER,
      defaultDistTag: null,
      summary: retirement.message,
      stableCandidate: false,
      blockers: [],
      docsAligned: true,
      ciCovered: false,
      productIntegrated: false,
      recommendedAction: 'retired',
      releaseTag: null,
      retirement,
    });
  }

  const counts = {
    publishLatest: packages.filter((entry) => entry.recommendedAction === 'publish-latest').length,
    publishNext: packages.filter((entry) => entry.recommendedAction === 'publish-next').length,
    holdInternal: packages.filter((entry) => entry.recommendedAction === 'hold-internal').length,
    holdExperimental: packages.filter((entry) => entry.recommendedAction === 'hold-experimental').length,
    retired: packages.filter((entry) => entry.recommendedAction === 'retired').length,
    byWave: sortReleaseEntries(
      RELEASE_WAVE_VALUES.map((wave, index) => ({
        name: wave,
        releaseWave: wave,
        publishOrder: index + 1,
      })),
    ).reduce((acc, waveEntry) => {
      acc[waveEntry.releaseWave] = packages.filter((entry) => entry.releaseWave === waveEntry.releaseWave).length;
      return acc;
    }, {}),
  };

  return {
    generatedAt: new Date().toISOString(),
    counts,
    packages: sortReleaseEntries(packages),
  };
}

export function validatePackageRetirements(surface, retirements) {
  const findings = [];
  const activeNames = new Set(surface.packages.map((entry) => entry.name));
  const seen = new Set();

  for (const entry of retirements.packages ?? []) {
    if (!entry || typeof entry !== 'object') {
      findings.push('Retirement manifest entries must be objects.');
      continue;
    }

    if (typeof entry.name !== 'string' || entry.name.length === 0) {
      findings.push('Retirement manifest entries must include a non-empty package name.');
      continue;
    }

    if (seen.has(entry.name)) {
      findings.push(`Retirement manifest contains duplicate package entry: ${entry.name}`);
    }
    seen.add(entry.name);

    if (!RETIREMENT_STATUS_VALUES.has(entry.status)) {
      findings.push(`Unsupported retirement status for ${entry.name}: ${entry.status}`);
    }

    if (activeNames.has(entry.name)) {
      findings.push(`Retired package ${entry.name} is still listed in config/package-surface.json.`);
    }

    if (typeof entry.message !== 'string' || entry.message.length < 20) {
      findings.push(`Retired package ${entry.name} must include a meaningful deprecation message.`);
    }
  }

  return findings;
}
