import { describe, it, expect } from 'vitest';
import fs from 'node:fs';
import path from 'node:path';

const repoRoot = path.resolve(__dirname, '../../../..');
const packagesRoot = path.join(repoRoot, 'packages');

type PackageKind =
  | 'racing-domain'
  | 'racing-application'
  | 'racing-infrastructure'
  | 'racing-demo-infrastructure'
  | 'other';

interface TsFile {
  filePath: string;
  kind: PackageKind;
}

function classifyFile(filePath: string): PackageKind {
  const normalized = filePath.replace(/\\/g, '/');

  // Bounded-context domain lives under core/racing/domain
  if (normalized.includes('/core/racing/domain/')) {
    return 'racing-domain';
  }
  if (normalized.includes('/core/racing-application/')) {
    return 'racing-application';
  }
  if (normalized.includes('/core/racing-infrastructure/')) {
    return 'racing-infrastructure';
  }
  if (normalized.includes('/core/racing-demo-infrastructure/')) {
    return 'racing-demo-infrastructure';
  }

  return 'other';
}

function collectTsFiles(dir: string): TsFile[] {
  const entries = fs.readdirSync(dir, { withFileTypes: true });
  const files: TsFile[] = [];

  for (const entry of entries) {
    const fullPath = path.join(dir, entry.name);
    if (entry.isDirectory()) {
      files.push(...collectTsFiles(fullPath));
    } else if (entry.isFile()) {
      if (
        entry.name.endsWith('.ts') ||
        entry.name.endsWith('.tsx')
      ) {
        const kind = classifyFile(fullPath);
        if (kind !== 'other') {
          files.push({ filePath: fullPath, kind });
        }
      }
    }
  }

  return files;
}

interface ImportViolation {
  file: string;
  line: number;
  moduleSpecifier: string;
  reason: string;
}

function extractImportModule(line: string): string | null {
  const trimmed = line.trim();
  if (!trimmed.startsWith('import')) return null;

  // Handle: import ... from 'x';
  const fromMatch = trimmed.match(/from\s+['"](.*)['"]/);
  if (fromMatch) {
    return fromMatch[1] || null;
  }

  // Handle: import 'x';
  const sideEffectMatch = trimmed.match(/^import\s+['"](.*)['"]\s*;?$/);
  if (sideEffectMatch) {
    return sideEffectMatch[1] || null;
  }

  return null;
}

describe('Package dependency structure for racing slice', () => {
  const tsFiles = collectTsFiles(packagesRoot);

  it('enforces import boundaries for racing-domain', () => {
    const violations: ImportViolation[] = [];

    const forbiddenPrefixes = [
      '@gridpilot/racing-application',
      '@gridpilot/racing-infrastructure',
      '@gridpilot/racing-demo-infrastructure',
      'apps/',
      '@/',
      'react',
      'next',
      'electron',
    ];

    for (const { filePath, kind } of tsFiles) {
      if (kind !== 'racing-domain') continue;

      const content = fs.readFileSync(filePath, 'utf8');
      const lines = content.split(/\r?\n/);

      lines.forEach((line, index) => {
        const moduleSpecifier = extractImportModule(line);
        if (!moduleSpecifier) return;

        for (const prefix of forbiddenPrefixes) {
          if (moduleSpecifier === prefix || moduleSpecifier.startsWith(prefix)) {
            violations.push({
              file: filePath,
              line: index + 1,
              moduleSpecifier,
              reason: 'racing-domain must not depend on application, infrastructure, apps, or UI frameworks',
            });
          }
        }
      });
    }

    if (violations.length > 0) {
      const message =
        'Found forbidden imports in racing domain layer (core/racing/domain):\n' +
        violations
          .map(
            (v) =>
              `- ${v.file}:${v.line} :: import '${v.moduleSpecifier}' // ${v.reason}`,
          )
          .join('\n');
      expect(message).toBe('');
    } else {
      expect(violations).toEqual([]);
    }
  });

  it('enforces import boundaries for racing-application', () => {
    const violations: ImportViolation[] = [];

    const forbiddenPrefixes = [
      '@gridpilot/racing-infrastructure',
      '@gridpilot/racing-demo-infrastructure',
      'apps/',
      '@/',
    ];

    const allowedPrefixes = [
      '@gridpilot/racing',
      '@gridpilot/shared-result',
      '@gridpilot/identity',
    ];

    for (const { filePath, kind } of tsFiles) {
      if (kind !== 'racing-application') continue;

      const content = fs.readFileSync(filePath, 'utf8');
      const lines = content.split(/\r?\n/);

      lines.forEach((line, index) => {
        const moduleSpecifier = extractImportModule(line);
        if (!moduleSpecifier) return;

        for (const prefix of forbiddenPrefixes) {
          if (moduleSpecifier === prefix || moduleSpecifier.startsWith(prefix)) {
            violations.push({
              file: filePath,
              line: index + 1,
              moduleSpecifier,
              reason: 'racing-application must not depend on infrastructure or apps',
            });
          }
        }

        if (moduleSpecifier.startsWith('@gridpilot/')) {
          const isAllowed = allowedPrefixes.some((prefix) =>
            moduleSpecifier === prefix || moduleSpecifier.startsWith(prefix),
          );
          if (!isAllowed) {
            violations.push({
              file: filePath,
              line: index + 1,
              moduleSpecifier,
              reason: 'racing-application should only depend on domain, shared-result, or other domain packages',
            });
          }
        }
      });
    }

    if (violations.length > 0) {
      const message =
        'Found forbidden imports in core/racing-application:\n' +
        violations
          .map(
            (v) =>
              `- ${v.file}:${v.line} :: import '${v.moduleSpecifier}' // ${v.reason}`,
          )
          .join('\n');
      expect(message).toBe('');
    } else {
      expect(violations).toEqual([]);
    }
  });

  it('enforces import boundaries for racing infrastructure packages', () => {
    const violations: ImportViolation[] = [];

    const forbiddenPrefixes = ['apps/', '@/'];

    const allowedPrefixes = [
      '@gridpilot/racing',
      '@gridpilot/shared-result',
      '@gridpilot/testing-support',
      '@gridpilot/social',
    ];

    for (const { filePath, kind } of tsFiles) {
      if (
        kind !== 'racing-infrastructure' &&
        kind !== 'racing-demo-infrastructure'
      ) {
        continue;
      }

      const content = fs.readFileSync(filePath, 'utf8');
      const lines = content.split(/\r?\n/);

      lines.forEach((line, index) => {
        const moduleSpecifier = extractImportModule(line);
        if (!moduleSpecifier) return;

        for (const prefix of forbiddenPrefixes) {
          if (moduleSpecifier === prefix || moduleSpecifier.startsWith(prefix)) {
            violations.push({
              file: filePath,
              line: index + 1,
              moduleSpecifier,
              reason: 'racing infrastructure must not depend on apps or @/ aliases',
            });
          }
        }

        if (moduleSpecifier.startsWith('@gridpilot/')) {
          const isAllowed = allowedPrefixes.some((prefix) =>
            moduleSpecifier === prefix || moduleSpecifier.startsWith(prefix),
          );
          if (!isAllowed) {
            violations.push({
              file: filePath,
              line: index + 1,
              moduleSpecifier,
              reason: 'racing infrastructure should depend only on domain, shared-result, or testing-support',
            });
          }
        }
      });
    }

    if (violations.length > 0) {
      const message =
        'Found forbidden imports in racing infrastructure packages:\n' +
        violations
          .map(
            (v) =>
              `- ${v.file}:${v.line} :: import '${v.moduleSpecifier}' // ${v.reason}`,
          )
          .join('\n');
      expect(message).toBe('');
    } else {
      expect(violations).toEqual([]);
    }
  });
});