import { constants } from 'node:fs';
import { access } from 'node:fs/promises';
import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

import { Command } from 'commander';

import { detectProjectKind } from './detect.js';
import { scaffoldQualityRails } from './scaffolder.js';
import type { ProjectKind, QualityProfile, RailsConfig } from './types.js';

const VALID_PROFILES: readonly QualityProfile[] = ['strict', 'standard', 'minimal'];

async function fileExists(filePath: string): Promise<boolean> {
  try {
    await access(filePath, constants.F_OK);
    return true;
  } catch {
    return false;
  }
}

function parseProfile(rawProfile: string): QualityProfile {
  if (VALID_PROFILES.includes(rawProfile as QualityProfile)) {
    return rawProfile as QualityProfile;
  }
  throw new Error(`Invalid profile: ${rawProfile}. Use one of ${VALID_PROFILES.join(', ')}.`);
}

function defaultLinters(kind: ProjectKind): string[] {
  if (kind === 'node') {
    return ['eslint', 'biome'];
  }

  if (kind === 'python') {
    return ['ruff'];
  }

  if (kind === 'rust') {
    return ['clippy'];
  }

  return [];
}

function defaultFormatters(kind: ProjectKind): string[] {
  if (kind === 'node') {
    return ['prettier'];
  }

  if (kind === 'python') {
    return ['black'];
  }

  if (kind === 'rust') {
    return ['rustfmt'];
  }

  return [];
}

function expectedFilesForKind(kind: ProjectKind): string[] {
  if (kind === 'node') {
    return ['.eslintrc', 'biome.json', '.githooks/pre-commit', 'PR-CHECKLIST.md'];
  }

  if (kind === 'python') {
    return ['pyproject.toml', '.githooks/pre-commit', 'PR-CHECKLIST.md'];
  }

  if (kind === 'rust') {
    return ['rustfmt.toml', '.githooks/pre-commit', 'PR-CHECKLIST.md'];
  }

  return ['.githooks/pre-commit', 'PR-CHECKLIST.md'];
}

function printScaffoldResult(
  config: RailsConfig,
  filesWritten: string[],
  warnings: string[],
  commandsToRun: string[],
): void {
  console.log(`[quality-rails] initialized at ${config.projectPath}`);
  console.log(`kind=${config.kind} profile=${config.profile}`);

  if (filesWritten.length > 0) {
    console.log('files written:');
    for (const filePath of filesWritten) {
      console.log(`  - ${filePath}`);
    }
  }

  if (commandsToRun.length > 0) {
    console.log('run next:');
    for (const command of commandsToRun) {
      console.log(`  - ${command}`);
    }
  }

  if (warnings.length > 0) {
    console.log('warnings:');
    for (const warning of warnings) {
      console.log(`  - ${warning}`);
    }
  }
}

/**
 * Register quality-rails subcommands on an existing Commander program.
 * This avoids cross-package Commander version mismatches by using the
 * caller's Command instance directly.
 */
export function registerQualityRails(parent: Command): void {
  buildQualityRailsCommand(
    parent.command('quality-rails').description('Manage quality rails scaffolding'),
  );
}

export function createQualityRailsCli(): Command {
  const program = new Command('mosaic');
  buildQualityRailsCommand(
    program.command('quality-rails').description('Manage quality rails scaffolding'),
  );
  return program;
}

function buildQualityRailsCommand(qualityRails: Command): void {
  qualityRails
    .command('init')
    .requiredOption('--project <path>', 'Project path')
    .option('--profile <profile>', 'strict|standard|minimal', 'standard')
    .action(async (options: { project: string; profile: string }) => {
      const profile = parseProfile(options.profile);
      const projectPath = resolve(options.project);
      const kind = await detectProjectKind(projectPath);

      const config: RailsConfig = {
        projectPath,
        kind,
        profile,
        linters: defaultLinters(kind),
        formatters: defaultFormatters(kind),
        hooks: true,
      };

      const result = await scaffoldQualityRails(config);
      printScaffoldResult(config, result.filesWritten, result.warnings, result.commandsToRun);
    });

  qualityRails
    .command('check')
    .requiredOption('--project <path>', 'Project path')
    .action(async (options: { project: string }) => {
      const projectPath = resolve(options.project);
      const kind = await detectProjectKind(projectPath);
      const expected = expectedFilesForKind(kind);
      const missing: string[] = [];

      for (const relativePath of expected) {
        const exists = await fileExists(resolve(projectPath, relativePath));
        if (!exists) {
          missing.push(relativePath);
        }
      }

      if (missing.length > 0) {
        console.error('[quality-rails] missing files:');
        for (const relativePath of missing) {
          console.error(`  - ${relativePath}`);
        }
        process.exitCode = 1;
        return;
      }

      console.log(`[quality-rails] all expected files present for ${kind} project`);
    });

  qualityRails
    .command('doctor')
    .requiredOption('--project <path>', 'Project path')
    .action(async (options: { project: string }) => {
      const projectPath = resolve(options.project);
      const kind = await detectProjectKind(projectPath);
      const expected = expectedFilesForKind(kind);

      console.log(`[quality-rails] doctor for ${projectPath}`);
      console.log(`detected project kind: ${kind}`);

      for (const relativePath of expected) {
        const exists = await fileExists(resolve(projectPath, relativePath));
        console.log(`  - ${exists ? 'ok' : 'missing'}: ${relativePath}`);
      }

      if (kind === 'unknown') {
        console.log(
          'recommendation: add package.json, pyproject.toml, or Cargo.toml for better defaults.',
        );
      }
    });
}

export async function runQualityRailsCli(argv: string[] = process.argv): Promise<void> {
  const program = createQualityRailsCli();
  await program.parseAsync(argv);
}

const entryPath = process.argv[1] ? resolve(process.argv[1]) : '';
if (entryPath.length > 0 && entryPath === fileURLToPath(import.meta.url)) {
  runQualityRailsCli().catch((error: unknown) => {
    console.error(error instanceof Error ? error.message : String(error));
    process.exit(1);
  });
}
