/**
 * Skills Sync - Interactive checklist to manage skills in working directory
 */

import { existsSync, readdirSync, cpSync, mkdirSync, rmSync } from 'node:fs';
import { join } from 'node:path';
import * as p from '@clack/prompts';
import { PROJECT_SKILLS_DIR, BUNDLED_SKILLS_DIR, GLOBAL_SKILLS_DIR, SKILLS_SH_DIR, WORKING_SKILLS_DIR, parseSkillFile } from './loader.js';

const HOME = process.env.HOME || process.env.USERPROFILE || '';
const TARGET_DIR = WORKING_SKILLS_DIR;

// Skill source directories
const CLAWDHUB_DIR = join(HOME, 'clawd', 'skills');      // ~/clawd/skills (ClawdHub)
const VERCEL_DIR = join(HOME, '.agents', 'skills');      // ~/.agents/skills (Vercel)

interface SkillInfo {
  name: string;
  description: string;
  source: 'builtin' | 'global' | 'clawdhub' | 'vercel';
  sourcePath: string;
  installed: boolean;
}

/**
 * Discover all available skills from all sources
 */
function discoverSkills(): SkillInfo[] {
  const skills: SkillInfo[] = [];
  const seen = new Set<string>();
  
  // Get existing skills in target
  const installedSkills = new Set<string>();
  if (existsSync(TARGET_DIR)) {
    for (const entry of readdirSync(TARGET_DIR, { withFileTypes: true })) {
      if (entry.isDirectory() && !entry.name.startsWith('.')) {
        installedSkills.add(entry.name);
      }
    }
  }
  
  // Helper to add skills from a directory
  const addFromDir = (dir: string, source: SkillInfo['source']) => {
    if (!existsSync(dir)) return;
    
    try {
      for (const entry of readdirSync(dir, { withFileTypes: true })) {
        if (!entry.isDirectory() || entry.name.startsWith('.')) continue;
        if (seen.has(entry.name)) continue;
        
        const skillPath = join(dir, entry.name, 'SKILL.md');
        const skill = parseSkillFile(skillPath);
        
        if (skill) {
          seen.add(entry.name);
          skills.push({
            name: entry.name,
            description: skill.description || '',
            source,
            sourcePath: join(dir, entry.name),
            installed: installedSkills.has(entry.name),
          });
        }
      }
    } catch (e) {
      // Ignore errors reading directories
    }
  };
  
  // Discover from all sources (order matters - first source wins for duplicates).
  // Priority matches the loader hierarchy: project (.skills/) > bundled (skills/) > external.
  addFromDir(PROJECT_SKILLS_DIR, 'builtin'); // .skills/ project overrides
  addFromDir(BUNDLED_SKILLS_DIR, 'builtin'); // skills/ bundled with repo
  // ~/.letta/skills/ uses a category/skill-name nested structure (tools/, meta/, letta/, etc.)
  if (existsSync(GLOBAL_SKILLS_DIR)) {
    try {
      for (const cat of readdirSync(GLOBAL_SKILLS_DIR, { withFileTypes: true })) {
        if (cat.isDirectory() && !cat.name.startsWith('.')) {
          addFromDir(join(GLOBAL_SKILLS_DIR, cat.name), 'global');
        }
      }
    } catch { /* ignore */ }
  }
  addFromDir(CLAWDHUB_DIR, 'clawdhub');
  addFromDir(VERCEL_DIR, 'vercel');
  
  return skills.sort((a, b) => a.name.localeCompare(b.name));
}

/**
 * Interactive skills sync with checklist
 */
export async function runSkillsSync(): Promise<void> {
  p.intro('🔄 Skills Sync');
  
  const skills = discoverSkills();
  
  if (skills.length === 0) {
    p.note(
      'No skills found.\n\n' +
      'Install skills with:\n' +
      '  npm run skill:install <name>     (ClawdHub)\n' +
      '  npm run skills:add <owner/repo>  (skills.sh)',
      'No skills available'
    );
    p.outro('');
    return;
  }
  
  const installedCount = skills.filter(s => s.installed).length;
  
  p.log.info(`Target: ${TARGET_DIR}`);
  p.log.info(`Found ${skills.length} skills (${installedCount} installed)`);
  
  // Check which sources exist
  const hasBuiltin = skills.some(s => s.source === 'builtin');
  const hasGlobal = skills.some(s => s.source === 'global');
  const hasClawdhub = existsSync(CLAWDHUB_DIR) && skills.some(s => s.source === 'clawdhub');
  const hasVercel = existsSync(VERCEL_DIR) && skills.some(s => s.source === 'vercel');

  // Build options grouped by source with headers
  const options: Array<{ value: string; label: string; hint: string }> = [];

  // Add Letta global skills section (~/.letta/skills/)
  if (hasGlobal) {
    options.push({ value: '__header_global__', label: `── Letta Skills ── (~/.letta/skills)`, hint: '' });
    for (const skill of skills.filter(s => s.source === 'global')) {
      const desc = skill.description || '';
      options.push({
        value: skill.name,
        label: `✦ ${skill.name}`,
        hint: desc.length > 60 ? desc.slice(0, 57) + '...' : desc,
      });
    }
  }

  // Add ClawdHub skills section
  if (hasClawdhub) {
    options.push({ value: '__header_clawdhub__', label: '── ClawdHub Skills ── (~/clawd/skills)', hint: '' });
    for (const skill of skills.filter(s => s.source === 'clawdhub')) {
      const desc = skill.description || '';
      options.push({
        value: skill.name,
        label: `🦞 ${skill.name}`,
        hint: desc.length > 60 ? desc.slice(0, 57) + '...' : desc,
      });
    }
  }

  // Add Vercel skills section
  if (hasVercel) {
    options.push({ value: '__header_vercel__', label: '── Vercel Skills ── (~/.agents/skills)', hint: '' });
    for (const skill of skills.filter(s => s.source === 'vercel')) {
      const desc = skill.description || '';
      options.push({
        value: skill.name,
        label: `🔼 ${skill.name}`,
        hint: desc.length > 60 ? desc.slice(0, 57) + '...' : desc,
      });
    }
  }

  // Add built-in skills section
  if (hasBuiltin) {
    options.push({ value: '__header_builtin__', label: '── Built-in Skills ──', hint: '' });
    for (const skill of skills.filter(s => s.source === 'builtin')) {
      const desc = skill.description || '';
      options.push({
        value: skill.name,
        label: `📦 ${skill.name}`,
        hint: desc.length > 60 ? desc.slice(0, 57) + '...' : desc,
      });
    }
  }
  
  // Pre-check currently installed skills so the user edits from current state
  const currentlyInstalled = skills.filter(s => s.installed).map(s => s.name);
  const selected = await p.multiselect({
    message: 'Enable skills (space=toggle, enter=confirm):',
    options,
    initialValues: currentlyInstalled,
    required: false,
  });
  
  if (p.isCancel(selected)) {
    p.cancel('Cancelled');
    return;
  }
  
  // Filter out header items
  const selectedSkills = (selected as string[]).filter(s => !s.startsWith('__header_'));
  const selectedSet = new Set(selectedSkills);
  
  // Determine what to add and remove
  const toAdd = skills.filter(s => selectedSet.has(s.name) && !s.installed);
  const toRemove = skills.filter(s => !selectedSet.has(s.name) && s.installed);
  
  if (toAdd.length === 0 && toRemove.length === 0) {
    p.log.info('No changes needed');
    p.log.info(`Skills directory: ${TARGET_DIR}`);
    p.outro('✨ Done!');
    return;
  }
  
  // Confirm changes
  const confirmMsg = [];
  if (toAdd.length > 0) confirmMsg.push(`Add ${toAdd.length} skill(s)`);
  if (toRemove.length > 0) confirmMsg.push(`Remove ${toRemove.length} skill(s)`);
  
  const confirmed = await p.confirm({
    message: `${confirmMsg.join(', ')}?`,
  });
  
  if (!confirmed || p.isCancel(confirmed)) {
    p.cancel('Cancelled');
    return;
  }
  
  // Ensure target directory exists
  mkdirSync(TARGET_DIR, { recursive: true });
  
  // Add new skills
  for (const skill of toAdd) {
    const dest = join(TARGET_DIR, skill.name);
    try {
      cpSync(skill.sourcePath, dest, { recursive: true });
      p.log.success(`Added: ${skill.name}`);
    } catch (e) {
      p.log.error(`Failed to add ${skill.name}: ${e}`);
    }
  }
  
  // Remove skills
  for (const skill of toRemove) {
    const dest = join(TARGET_DIR, skill.name);
    try {
      rmSync(dest, { recursive: true, force: true });
      p.log.warn(`Removed: ${skill.name}`);
    } catch (e) {
      p.log.error(`Failed to remove ${skill.name}: ${e}`);
    }
  }
  
  p.log.info(`Skills directory: ${TARGET_DIR}`);
  p.outro(`✨ Added ${toAdd.length}, removed ${toRemove.length} skill(s)`);
}

/**
 * Non-interactively disable a single skill by name.
 */
export function disableSkill(name: string): void {
  const dest = join(TARGET_DIR, name);
  if (!existsSync(dest)) {
    p.log.warn(`Skill '${name}' is not enabled.`);
    return;
  }
  try {
    rmSync(dest, { recursive: true, force: true });
    p.log.success(`Disabled skill '${name}'.`);
  } catch (e) {
    p.log.error(`Failed to disable '${name}': ${e}`);
    process.exit(1);
  }
}

/**
 * Non-interactively enable a single skill by name.
 * Searches BUNDLED_SKILLS_DIR, then GLOBAL_SKILLS_DIR, then SKILLS_SH_DIR.
 */
export function enableSkill(name: string): void {
  mkdirSync(TARGET_DIR, { recursive: true });

  const dest = join(TARGET_DIR, name);
  if (existsSync(dest)) {
    p.log.warn(`Skill '${name}' is already enabled.`);
    return;
  }

  // Build search list: flat dirs first, then nested GLOBAL_SKILLS_DIR categories
  const flatDirs = [PROJECT_SKILLS_DIR, BUNDLED_SKILLS_DIR, SKILLS_SH_DIR];
  const searchDirs: string[] = [...flatDirs];
  if (existsSync(GLOBAL_SKILLS_DIR)) {
    try {
      for (const cat of readdirSync(GLOBAL_SKILLS_DIR, { withFileTypes: true })) {
        if (cat.isDirectory() && !cat.name.startsWith('.')) {
          searchDirs.push(join(GLOBAL_SKILLS_DIR, cat.name));
        }
      }
    } catch { /* ignore */ }
  }

  for (const dir of searchDirs) {
    const src = join(dir, name);
    if (existsSync(src) && existsSync(join(src, 'SKILL.md'))) {
      cpSync(src, dest, { recursive: true });
      p.log.success(`Enabled skill '${name}'.`);
      return;
    }
  }

  p.log.error(`Skill '${name}' not found. Run 'lettabot skills status' to see available skills.`);
  process.exit(1);
}
