import { join } from 'path';
import { execSync } from 'child_process';
import { platform } from 'os';
import chalk from 'chalk';
import { logger } from '../utils/logger.js';
import { pathExists, readJson } from '../utils/file-copier.js';
import {
  checkCLIOutdated,
  checkProjectOutdated,
  getInstalledCLIVersion
} from '../utils/version-checker.js';

const isWindows = platform() === 'win32';

/**
 * Check if morph-spec command is in PATH
 */
function isMorphSpecInPath() {
  try {
    const command = isWindows ? 'where morph-spec' : 'which morph-spec';
    execSync(command, { stdio: 'ignore' });
    return true;
  } catch {
    return false;
  }
}

/**
 * Detect if nvm-windows is installed
 */
function isUsingNvmWindows() {
  if (!isWindows) return false;

  try {
    const path = process.env.PATH || '';
    return path.includes('nvm4w') || path.includes('\\nvm\\');
  } catch {
    return false;
  }
}

/**
 * Get npm global prefix
 */
function getNpmGlobalPrefix() {
  try {
    const prefix = execSync('npm config get prefix', { encoding: 'utf8' }).trim();
    return prefix;
  } catch {
    return null;
  }
}

export async function doctorCommand() {
  const targetPath = process.cwd();

  logger.header('MORPH-SPEC Health Check');

  const checks = [];
  let hasErrors = false;
  let hasWarnings = false;

  // Check versions first
  const cliCheck = await checkCLIOutdated();
  const projectCheck = await checkProjectOutdated(targetPath);

  // CLI Version
  if (cliCheck.latest) {
    if (cliCheck.isOutdated) {
      checks.push({
        name: `CLI version (${cliCheck.current})`,
        status: 'warn',
        msg: `outdated, ${cliCheck.latest} available`
      });
      hasWarnings = true;
    } else {
      checks.push({
        name: `CLI version (${cliCheck.current})`,
        status: 'ok',
        msg: 'latest'
      });
    }
  } else {
    checks.push({
      name: `CLI version (${cliCheck.current})`,
      status: 'ok'
    });
  }

  // Project MORPH version
  if (projectCheck.current) {
    if (projectCheck.isOutdated) {
      checks.push({
        name: `Project MORPH version (${projectCheck.current})`,
        status: 'warn',
        msg: `outdated, ${projectCheck.cliVersion} available`
      });
      hasWarnings = true;
    } else {
      checks.push({
        name: `Project MORPH version (${projectCheck.current})`,
        status: 'ok'
      });
    }
  } else {
    checks.push({
      name: 'Project MORPH version',
      status: 'warn',
      msg: 'not found (legacy installation)'
    });
    hasWarnings = true;
  }

  // Check if morph-spec is in PATH
  const inPath = isMorphSpecInPath();
  if (inPath) {
    checks.push({
      name: 'morph-spec in PATH',
      status: 'ok'
    });
  } else {
    checks.push({
      name: 'morph-spec in PATH',
      status: 'warn',
      msg: 'command not found'
    });
    hasWarnings = true;
  }

  // Check CLAUDE.md
  const claudeMd = join(targetPath, 'CLAUDE.md');
  if (await pathExists(claudeMd)) {
    checks.push({ name: 'CLAUDE.md', status: 'ok' });
  } else {
    checks.push({ name: 'CLAUDE.md', status: 'missing' });
    hasErrors = true;
  }

  // Check .morph folder
  const morphPath = join(targetPath, '.morph');
  if (await pathExists(morphPath)) {
    checks.push({ name: '.morph/', status: 'ok' });
  } else {
    checks.push({ name: '.morph/', status: 'missing' });
    hasErrors = true;
  }

  // Check config.json
  const configPath = join(morphPath, 'config', 'config.json');
  if (await pathExists(configPath)) {
    try {
      const config = await readJson(configPath);
      if (config.project?.name) {
        checks.push({ name: 'config.json', status: 'ok' });
      } else {
        checks.push({ name: 'config.json', status: 'warn', msg: 'project.name not set' });
      }
    } catch {
      checks.push({ name: 'config.json', status: 'error', msg: 'invalid JSON' });
      hasErrors = true;
    }
  } else {
    checks.push({ name: 'config.json', status: 'missing' });
    hasErrors = true;
  }

  // Check agents.json
  const agentsPath = join(morphPath, 'config', 'agents.json');
  if (await pathExists(agentsPath)) {
    checks.push({ name: 'agents.json', status: 'ok' });
  } else {
    checks.push({ name: 'agents.json', status: 'missing' });
    hasErrors = true;
  }

  // Check standards
  const standardsPath = join(morphPath, 'standards');
  if (await pathExists(standardsPath)) {
    const codingStd = join(standardsPath, 'coding.md');
    const archStd = join(standardsPath, 'architecture.md');
    const azureStd = join(standardsPath, 'azure.md');

    const hasAll = await Promise.all([
      pathExists(codingStd),
      pathExists(archStd),
      pathExists(azureStd)
    ]).then(results => results.every(Boolean));

    if (hasAll) {
      checks.push({ name: 'standards/', status: 'ok' });
    } else {
      checks.push({ name: 'standards/', status: 'warn', msg: 'some files missing' });
    }
  } else {
    checks.push({ name: 'standards/', status: 'missing' });
    hasErrors = true;
  }

  // Check templates
  const templatesPath = join(morphPath, 'templates');
  if (await pathExists(templatesPath)) {
    checks.push({ name: 'templates/', status: 'ok' });
  } else {
    checks.push({ name: 'templates/', status: 'missing' });
    hasErrors = true;
  }

  // Check .claude folder
  const claudePath = join(targetPath, '.claude');
  if (await pathExists(claudePath)) {
    const commandsPath = join(claudePath, 'commands');
    const skillsPath = join(claudePath, 'skills');

    const hasCommands = await pathExists(commandsPath);
    const hasSkills = await pathExists(skillsPath);

    if (hasCommands && hasSkills) {
      checks.push({ name: '.claude/', status: 'ok' });
    } else {
      checks.push({ name: '.claude/', status: 'warn', msg: 'incomplete structure' });
    }
  } else {
    checks.push({ name: '.claude/', status: 'missing' });
    hasErrors = true;
  }

  // Display results
  for (const check of checks) {
    if (check.status === 'ok') {
      console.log(chalk.green(`  ✓ ${check.name}`));
    } else if (check.status === 'warn') {
      console.log(chalk.yellow(`  ⚠ ${check.name}`) + chalk.gray(` (${check.msg})`));
    } else if (check.status === 'missing') {
      console.log(chalk.red(`  ✗ ${check.name}`) + chalk.gray(' (missing)'));
    } else if (check.status === 'error') {
      console.log(chalk.red(`  ✗ ${check.name}`) + chalk.gray(` (${check.msg})`));
    }
  }

  logger.blank();

  if (hasErrors) {
    logger.error('Some checks failed. Run "morph-spec init --force" to fix.');
    process.exit(1);
  } else if (hasWarnings) {
    logger.warn('Some checks need attention.');
    logger.blank();

    if (!inPath) {
      const usingNvm = isUsingNvmWindows();
      const prefix = getNpmGlobalPrefix();

      if (usingNvm && prefix) {
        logger.info('PATH Issue Detected (nvm-windows):');
        logger.dim(`  Add to PATH: ${prefix}`);
        logger.blank();
        logger.dim('  PowerShell (as Administrator):');
        console.log(chalk.gray(`    [Environment]::SetEnvironmentVariable("Path", [Environment]::GetEnvironmentVariable("Path", "User") + ";${prefix}", "User")`));
        logger.blank();
        logger.dim('  Or use npx instead:');
        logger.dim('    npx @polymorphism-tech/morph-spec init');
        logger.blank();
      } else if (!inPath) {
        logger.info('morph-spec not in PATH:');
        logger.dim('  Use npx instead:');
        logger.dim('    npx @polymorphism-tech/morph-spec init');
        logger.blank();
      }
    }

    if (cliCheck.isOutdated || projectCheck.isOutdated) {
      logger.info('To update:');
      logger.dim('  1. Update the CLI: npm install -g @polymorphism-tech/morph-spec@latest');
      logger.dim('  2. Update the project: morph-spec update');
      logger.blank();
    }
  } else {
    logger.success('All checks passed!');
  }
}
