import { join } from 'path';
import fs from 'fs-extra';
import ora from 'ora';
import chalk from 'chalk';
import { logger } from '../utils/logger.js';
import {
  getContentDir,
  copyDirectory,
  copyFile,
  pathExists,
  readJson,
  writeJson,
  ensureDir,
  writeFile,
  readFile,
  updateGitignore,
  createSymlink
} from '../utils/file-copier.js';
import { saveProjectMorphVersion, getInstalledCLIVersion } from '../utils/version-checker.js';

export async function initCommand(options) {
  const targetPath = options.path || process.cwd();
  const claudeMdPath = join(import.meta.dirname, '..', '..', 'CLAUDE.md');

  logger.header('MORPH-SPEC Framework Installation');
  logger.dim(`Target: ${targetPath}`);
  logger.blank();

  // Check if already initialized
  const morphPath = join(targetPath, '.morph');
  if (await pathExists(morphPath)) {
    if (!options.force) {
      logger.warn('MORPH already initialized in this directory.');
      logger.dim('Use --force to overwrite existing installation.');
      process.exit(1);
    }
    logger.warn('Overwriting existing MORPH installation...');
  }

  const spinner = ora('Installing MORPH-SPEC...').start();

  try {
    // 1. Copy CLAUDE.md
    spinner.text = 'Copying CLAUDE.md...';
    const claudeMdDest = join(targetPath, 'CLAUDE.md');

    // Backup existing CLAUDE.md if not from MORPH
    if (await pathExists(claudeMdDest)) {
      const existingContent = await readFile(claudeMdDest);
      if (!existingContent.includes('MORPH-SPEC')) {
        await copyFile(claudeMdDest, `${claudeMdDest}.backup`);
        logger.dim('Backed up existing CLAUDE.md');
      }
    }
    await copyFile(claudeMdPath, claudeMdDest);

    // 2. Create .morph directory structure
    spinner.text = 'Creating .morph structure...';
    const projectPath = join(morphPath, 'project');
    const configDir = join(morphPath, 'config');
    await ensureDir(join(projectPath, 'context'));
    await ensureDir(join(projectPath, 'standards'));
    await ensureDir(join(projectPath, 'outputs'));
    await ensureDir(configDir);

    // 3. Create config.json
    spinner.text = 'Generating config.json...';
    const configPath = join(configDir, 'config.json');
    const dirName = targetPath.split(/[\\/]/).pop();

    const config = {
      framework: 'global',
      frameworkVersion: getInstalledCLIVersion(),
      project: {
        name: dirName,
        stack: 'unknown',
        architecture: 'unknown'
      }
    };

    await writeJson(configPath, config);

    // 4. Create context/README.md
    spinner.text = 'Creating context README...';
    const contextReadme = join(projectPath, 'context', 'README.md');
    const readmeContent = `# ${dirName} - Project Context

## Overview

This file will be auto-updated by MORPH-SPEC detection system.

## Stack

Run \`morph-spec detect\` to analyze your project.

## Technologies

(To be detected)

## Architecture

(To be detected)
`;
    await writeFile(contextReadme, readmeContent);

    // 5. Copy templates from content
    spinner.text = 'Copying templates...';
    const contentDir = getContentDir();
    const templatesSrc = join(contentDir, '.morph', 'templates');
    const templatesDest = join(morphPath, 'templates');
    if (await pathExists(templatesSrc)) {
      await copyDirectory(templatesSrc, templatesDest);
    }

    // 6. Copy standards from content
    spinner.text = 'Copying standards...';
    const standardsSrc = join(contentDir, '.morph', 'standards');
    const standardsDest = join(morphPath, 'standards');
    if (await pathExists(standardsSrc)) {
      await copyDirectory(standardsSrc, standardsDest);
    }

    // 7. Copy agents.json
    spinner.text = 'Copying agents configuration...';
    const agentsSrc = join(contentDir, '.morph', 'config', 'agents.json');
    const agentsDest = join(configDir, 'agents.json');
    if (await pathExists(agentsSrc)) {
      await copyFile(agentsSrc, agentsDest);
    }

    // 8. Copy azure-pricing files
    spinner.text = 'Copying Azure pricing data...';
    const pricingSrc = join(contentDir, '.morph', 'config', 'azure-pricing.json');
    const pricingDest = join(configDir, 'azure-pricing.json');
    if (await pathExists(pricingSrc)) {
      await copyFile(pricingSrc, pricingDest);
    }
    const pricingSchemaSrc = join(contentDir, '.morph', 'config', 'azure-pricing.schema.json');
    const pricingSchemaDest = join(configDir, 'azure-pricing.schema.json');
    if (await pathExists(pricingSchemaSrc)) {
      await copyFile(pricingSchemaSrc, pricingSchemaDest);
    }

    // 9. Copy .claude commands and create symlinks for skills
    spinner.text = 'Setting up Claude Code integration...';
    const claudeSrc = join(contentDir, '.claude');
    const claudeDest = join(targetPath, '.claude');

    let symlinkCount = 0;
    let copyCount = 0;

    if (await pathExists(claudeSrc)) {
      // Copy commands directory (slash commands)
      const commandsSrc = join(claudeSrc, 'commands');
      const commandsDest = join(claudeDest, 'commands');
      if (await pathExists(commandsSrc)) {
        await copyDirectory(commandsSrc, commandsDest);
      }

      // Create symlinks for skills (or copy if symlink fails)
      const skillsSrc = join(claudeSrc, 'skills');
      const skillsDest = join(claudeDest, 'skills');

      if (await pathExists(skillsSrc)) {
        // Ensure skills destination directory exists
        await ensureDir(skillsDest);

        // Recursively walk through skills directory
        const walkDir = async (dir, basePath) => {
          const entries = await fs.readdir(dir, { withFileTypes: true });

          for (const entry of entries) {
            const srcPath = join(dir, entry.name);
            const relPath = join(basePath, entry.name);
            const destPath = join(skillsDest, relPath);

            if (entry.isDirectory()) {
              await ensureDir(destPath);
              await walkDir(srcPath, relPath);
            } else if (entry.isFile() && entry.name.endsWith('.md')) {
              // Create symlink for skill files
              const result = await createSymlink(srcPath, destPath, 'file');
              if (result === 'symlink') {
                symlinkCount++;
              } else {
                copyCount++;
              }
            }
          }
        };

        await walkDir(skillsSrc, '');
      }
    }

    // 10. Save version info
    spinner.text = 'Saving version info...';
    const cliVersion = getInstalledCLIVersion();
    await saveProjectMorphVersion(targetPath, cliVersion);

    // 11. Update .gitignore
    spinner.text = 'Updating .gitignore...';
    await updateGitignore(targetPath);

    spinner.succeed('MORPH-SPEC installed successfully!');

    // Show next steps
    logger.blank();
    logger.success(`MORPH-SPEC v${cliVersion} installed successfully!`);
    logger.blank();
    logger.header('Next Steps');

    logger.step(1, 'Run detection: morph-spec detect');
    logger.step(2, 'Review .morph/config.json and .morph/project/standards/inferred.md');
    logger.step(3, 'Open project in VS Code with Claude Code');
    logger.step(4, 'Start your first feature:');
    logger.blank();
    logger.box([
      'Ask Claude Code to implement a feature'
    ]);
    logger.blank();

    logger.info('Files installed:');
    logger.dim(`  ✓ CLAUDE.md`);
    logger.dim(`  ✓ .morph/config/ (config.json, agents.json, azure-pricing.json)`);
    logger.dim(`  ✓ .morph/standards/ (coding.md, architecture.md, azure.md, ...)`);
    logger.dim(`  ✓ .morph/templates/ (Bicep, integrations, saas, ...)`);
    logger.dim(`  ✓ .morph/project/ (context, standards, outputs)`);
    logger.dim(`  ✓ .claude/commands/ (slash commands)`);

    if (symlinkCount > 0) {
      logger.dim(`  ✓ .claude/skills/ (${symlinkCount} symlinked)`);
    } else if (copyCount > 0) {
      logger.dim(`  ✓ .claude/skills/ (${copyCount} copied)`);
      logger.warn(`  ⚠ Symlinks not supported (copied instead). Skills won't auto-update.`);
    }

    logger.blank();

  } catch (error) {
    spinner.fail('Installation failed');
    logger.error(error.message);
    process.exit(1);
  }
}
