/**
 * Import-boundary law (Bottom-Up §06 dependency law; Critics §12).
 *
 * Each package declares exactly which other @insignia/iios-* packages it MAY
 * import. Anything else is a violation. This is an allowlist, not a ladder —
 * it correctly forbids sibling imports (e.g. the SDK must never import the
 * backend service, and vice-versa). Runnable as `pnpm boundary` (CI) + a test.
 */
import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join, resolve } from 'node:path';

const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');

/** package -> the @insignia/iios-* packages it is allowed to import. */
const ALLOWED = {
  '@insignia/iios-contracts': [],
  '@insignia/iios-testkit': ['@insignia/iios-contracts'],
  '@insignia/iios-kernel-client': ['@insignia/iios-contracts'],
  '@insignia/iios-adapter-sdk': ['@insignia/iios-contracts'],
  '@insignia/iios-service': ['@insignia/iios-contracts', '@insignia/iios-testkit', '@insignia/iios-adapter-sdk'],
  '@insignia/iios-message-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'],
  // Core is transport-free; only ./adapters/kernel may import the kernel client.
  // This map is package-level and cannot express that subpath rule — the finer
  // constraint is enforced by packages/iios-messaging-ui/src/boundary.test.ts.
  '@insignia/iios-messaging-ui': ['@insignia/iios-kernel-client'],
  '@insignia/iios-inbox-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'],
  '@insignia/iios-community-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'],
  '@insignia/iios-ai-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'],
  '@insignia/iios-meeting-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'],
  '@insignia/iios-support-web': [
    '@insignia/iios-contracts',
    '@insignia/iios-kernel-client',
    '@insignia/iios-message-web',
    '@insignia/iios-inbox-web',
  ],
};
const KNOWN = Object.keys(ALLOWED);

function listTsFiles(dir) {
  if (!existsSync(dir)) return [];
  const out = [];
  for (const entry of readdirSync(dir)) {
    if (entry === 'node_modules' || entry === 'dist') continue;
    const full = join(dir, entry);
    if (statSync(full).isDirectory()) out.push(...listTsFiles(full));
    else if (full.endsWith('.ts') || full.endsWith('.tsx')) out.push(full);
  }
  return out;
}

function packageOf(importPath) {
  return KNOWN.find((k) => importPath === k || importPath.startsWith(k + '/'));
}

export function findBoundaryViolations() {
  const pkgsDir = join(ROOT, 'packages');
  const violations = [];
  if (!existsSync(pkgsDir)) return violations;

  for (const pkg of readdirSync(pkgsDir)) {
    const pkgJsonPath = join(pkgsDir, pkg, 'package.json');
    if (!existsSync(pkgJsonPath)) continue;
    const name = JSON.parse(readFileSync(pkgJsonPath, 'utf8')).name;
    const allowed = ALLOWED[name];
    if (!allowed) continue; // not a governed package

    const patterns = [
      /from\s*['"](@insignia\/iios-[^'"]+)['"]/g,
      /import\s*['"](@insignia\/iios-[^'"]+)['"]/g,
      /import\s*\(\s*['"](@insignia\/iios-[^'"]+)['"]/g,
      /require\(\s*['"](@insignia\/iios-[^'"]+)['"]/g,
    ];
    for (const file of listTsFiles(join(pkgsDir, pkg, 'src'))) {
      const content = readFileSync(file, 'utf8');
      for (const re of patterns) {
        let m;
        while ((m = re.exec(content))) {
          const target = packageOf(m[1]);
          if (target && target !== name && !allowed.includes(target)) {
            violations.push({ file: file.replace(ROOT + '/', ''), from: name, imports: target });
          }
        }
      }
    }
  }
  return violations;
}

if (import.meta.url === `file://${process.argv[1]}`) {
  const violations = findBoundaryViolations();
  if (violations.length > 0) {
    console.error('✗ import-boundary violations (a package imports a forbidden dependency):');
    for (const v of violations) console.error(`  ${v.file}: ${v.from} → ${v.imports}`);
    process.exit(1);
  }
  console.log('✓ import-boundary: OK');
}
