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,
  createDirectoryLink
} from '../../utils/file-copier.js';
import { saveProjectMorphVersion, getInstalledCLIVersion } from '../../utils/version-checker.js';
import { installClaudeHooks } from '../../utils/hooks-installer.js';
import { AutoContextOrchestrator } from '../../core/orchestrator.js';
import { detectClaudeCode } from '../../llm/environment-detector.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');
    let templatesCopied = false;
    if (await pathExists(templatesSrc)) {
      await copyDirectory(templatesSrc, templatesDest);
      templatesCopied = true;
    }

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

    // 6b. Copy framework standards hierarchy (universal standards)
    spinner.text = 'Copying framework standards hierarchy...';
    const frameworkStandardsSrc = join(import.meta.dirname, '..', '..', '..', 'framework', 'standards');
    const frameworkStandardsDest = join(morphPath, 'standards');
    if (await pathExists(frameworkStandardsSrc)) {
      // Copy entire hierarchy (core/, backend/, frontend/, infrastructure/)
      await copyDirectory(frameworkStandardsSrc, frameworkStandardsDest);
      logger.dim('  ✓ Copied framework standards: core/, backend/, frontend/, infrastructure/');
    }

    // 7. Copy agents.json (sourced from framework root .morph/config/ — canonical single source of truth)
    spinner.text = 'Copying agents configuration...';
    const agentsSrc = join(import.meta.dirname, '..', '..', '..', '.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
    // Source: framework root .claude/ (canonical for all stacks)
    spinner.text = 'Setting up Claude Code integration...';
    const claudeSrc = join(import.meta.dirname, '..', '..', '..', '.claude');
    const claudeDest = join(targetPath, '.claude');

    let symlinkCount = 0;
    let copyCount = 0;
    let commandsCopied = false;

    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);
        commandsCopied = true;
      } else {
        logger.warn('  ⚠ .claude/commands/ source missing — commands not installed');
      }

      // Create directory links for skill categories (or copy if linking fails)
      const skillsSrc = join(claudeSrc, 'skills');
      const skillsDest = join(claudeDest, 'skills');

      if (await pathExists(skillsSrc)) {
        await ensureDir(skillsDest);

        const entries = await fs.readdir(skillsSrc, { withFileTypes: true });

        let linkedCategories = 0;
        let copiedCategories = 0;
        let totalSkillFiles = 0;

        // Link category directories (specialists/, infra/, checklists/, etc.)
        for (const entry of entries) {
          if (entry.isDirectory()) {
            const categorySrc = join(skillsSrc, entry.name);
            const categoryDest = join(skillsDest, entry.name);

            // Count .md files for reporting
            const categoryEntries = await fs.readdir(categorySrc, { withFileTypes: true });
            const mdFiles = categoryEntries.filter(e => e.isFile() && e.name.endsWith('.md'));
            totalSkillFiles += mdFiles.length;

            const result = await createDirectoryLink(categorySrc, categoryDest);
            if (result === 'copy') {
              copiedCategories++;
            } else {
              linkedCategories++;
            }
          } else if (entry.isFile() && entry.name.endsWith('.md')) {
            // Handle .md files in skills root
            totalSkillFiles++;
            const result = await createSymlink(join(skillsSrc, entry.name), join(skillsDest, entry.name), 'file');
            if (result === 'symlink') {
              linkedCategories++;
            } else {
              copiedCategories++;
            }
          }
        }

        symlinkCount = linkedCategories > 0 ? totalSkillFiles : 0;
        copyCount = linkedCategories === 0 ? totalSkillFiles : 0;
      }
    }

    // 9b. Install/update agent-teams hooks in .claude/settings.local.json
    spinner.text = 'Installing Claude Code hooks...';
    const hooksInstalled = await installClaudeHooks(targetPath);

    // 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, ...)`);
    if (templatesCopied) {
      logger.dim(`  ✓ .morph/templates/ (Bicep, integrations, saas, ...)`);
    }
    logger.dim(`  ✓ .morph/project/ (context, standards, outputs)`);
    if (commandsCopied) {
      logger.dim(`  ✓ .claude/commands/ (slash commands)`);
    }
    logger.dim(`  ✓ .claude/settings.local.json (agent-teams hooks)`);

    if (symlinkCount > 0) {
      const linkType = process.platform === 'win32' ? 'junction-linked' : 'symlinked';
      logger.dim(`  ✓ .claude/skills/ (${symlinkCount} ${linkType})`);
    } else if (copyCount > 0) {
      logger.dim(`  ✓ .claude/skills/ (${copyCount} copied)`);
      logger.warn(`  ⚠ Directory links not supported (copied instead). Skills won't auto-update.`);
    }

    logger.blank();

    // 12. Auto-detect project context (if Claude Code is available and not --skip-detection)
    if (!options.skipDetection && detectClaudeCode()) {
      logger.blank();
      logger.header('Auto-Detecting Project Context');
      logger.dim('Using Claude Code LLM to analyze your project...');
      logger.blank();

      try {
        const orchestrator = new AutoContextOrchestrator();
        const result = await orchestrator.execute(targetPath, {
          skipReview: false,
          fallbackOnError: true,
          wizardMode: options.wizard || false
        });

        if (result.success) {
          logger.success('Project context detected and saved to .morph/project.md');
        } else {
          logger.warn('Auto-detection incomplete. Run "morph-spec update" to retry.');
        }
      } catch (error) {
        logger.warn(`Auto-detection failed: ${error.message}`);
        logger.dim('You can run "morph-spec update" later to re-analyze your project.');
      }
    } else if (options.skipDetection) {
      logger.dim('\nSkipped auto-detection (--skip-detection flag)');
      logger.dim('Run "morph-spec update" later to analyze your project.');
    } else {
      logger.warn('\n⚠️  Claude Code not detected');
      logger.dim('Auto-detection requires Claude Code CLI.');
      logger.dim('Run "morph-spec update --wizard" to configure manually.');
    }

    logger.blank();

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