#!/usr/bin/env bun
/**
 * install-context.ts
 * TypeScript context installer for OAC Claude Code Plugin
 * 
 * Downloads context files from OpenAgents Control repository using git sparse-checkout
 * Supports profile-based installation (essential, standard, extended, specialized, all)
 */

import { existsSync, writeFileSync, readFileSync } from 'fs'
import { join, relative, dirname } from 'path'
import type { InstallOptions, InstallResult, Profile } from './types/registry'
import type { Manifest, ManifestComponent } from './types/manifest'
import { fetchRegistry, filterContextByProfile, filterContextByIds, getUniquePaths } from './utils/registry-fetcher'
import { sparseClone, copyFiles, cleanup, checkGitAvailable } from './utils/git-sparse'

// Configuration
const GITHUB_REPO = 'darrenhinde/OpenAgentsControl'
const GITHUB_BRANCH = 'main'
const CONTEXT_SOURCE_PATH = '.opencode/context'
const PLUGIN_ROOT = process.env.CLAUDE_PLUGIN_ROOT || process.cwd()
const CONTEXT_DIR = join(PLUGIN_ROOT, 'context')
const MANIFEST_FILE = join(PLUGIN_ROOT, '.context-manifest.json')

// Colors for output
const colors = {
  reset: '\x1b[0m',
  red: '\x1b[31m',
  green: '\x1b[32m',
  yellow: '\x1b[33m',
  blue: '\x1b[34m',
  cyan: '\x1b[36m',
  bold: '\x1b[1m',
}

// Logging helpers
const log = {
  info: (msg: string) => console.log(`${colors.blue}ℹ${colors.reset} ${msg}`),
  success: (msg: string) => console.log(`${colors.green}✓${colors.reset} ${msg}`),
  warning: (msg: string) => console.log(`${colors.yellow}⚠${colors.reset} ${msg}`),
  error: (msg: string) => console.error(`${colors.red}✗${colors.reset} ${msg}`),
  header: (msg: string) => console.log(`\n${colors.bold}${colors.cyan}${msg}${colors.reset}\n`),
}

/**
 * Main installation function
 */
export async function installContext(options: InstallOptions = {}): Promise<InstallResult> {
  const {
    profile = 'essential',
    customComponents = [],
    dryRun = false,
    force = false,
    verbose = false,
  } = options

  try {
    // Check dependencies
    if (!checkGitAvailable()) {
      throw new Error('Git is not available. Please install git.')
    }

    // Check if already installed
    if (existsSync(MANIFEST_FILE) && !force) {
      log.warning('Context already installed. Use --force to reinstall.')
      const manifest = JSON.parse(readFileSync(MANIFEST_FILE, 'utf-8')) as Manifest
      return {
        success: true,
        manifest: convertManifestToInstallManifest(manifest),
      }
    }

    log.header('Context Installer')
    log.info(`Profile: ${profile}`)
    log.info(`Repository: ${GITHUB_REPO}`)
    log.info(`Branch: ${GITHUB_BRANCH}`)
    log.info(`Dry run: ${dryRun}`)
    console.log('')

    // Fetch registry
    log.info('Fetching registry from GitHub...')
    const registry = await fetchRegistry({
      source: 'github',
      repository: GITHUB_REPO,
      branch: GITHUB_BRANCH,
    })
    log.success(`Registry version: ${registry.version}`)
    log.success(`Context components available: ${registry.components.contexts?.length || 0}`)
    console.log('')

    // Filter components by profile or custom IDs
    let components
    if (customComponents.length > 0) {
      log.info('Filtering by custom component IDs...')
      components = filterContextByIds(registry, customComponents)
    } else {
      log.info(`Filtering by profile: ${profile}`)
      components = filterContextByProfile(registry, profile)
    }

    log.success(`Selected ${components.length} components`)
    
    if (verbose) {
      console.log('')
      log.info('Components to install:')
      for (const component of components) {
        console.log(`  - ${component.id}: ${component.path}`)
      }
    }
    console.log('')

    if (dryRun) {
      log.info('Dry run mode - no files will be downloaded')
      return {
        success: true,
        manifest: {
          version: '1.0.0',
          profile: customComponents.length > 0 ? 'custom' : profile,
          source: {
            repository: GITHUB_REPO,
            branch: GITHUB_BRANCH,
            commit: 'dry-run',
            downloaded_at: new Date().toISOString(),
          },
          context: components.map((c) => ({
            id: c.id,
            name: c.name,
            path: c.path,
            local_path: join(CONTEXT_DIR, c.path.replace(`${CONTEXT_SOURCE_PATH}/`, '')),
            category: c.category,
          })),
        },
      }
    }

    // Get unique directory paths for sparse checkout
    const sparsePaths = getUniquePaths(components)
    
    if (verbose) {
      log.info('Sparse checkout paths:')
      for (const path of sparsePaths) {
        console.log(`  - ${path}`)
      }
      console.log('')
    }

    // Download using git sparse-checkout
    log.info('Downloading context files...')
    const tempDir = join(PLUGIN_ROOT, '.tmp-context-download')
    
    const cloneResult = await sparseClone({
      repository: GITHUB_REPO,
      branch: GITHUB_BRANCH,
      paths: sparsePaths,
      targetDir: tempDir,
      verbose,
    })

    if (!cloneResult.success) {
      throw new Error(`Git sparse clone failed: ${cloneResult.error}`)
    }

    log.success('Files downloaded successfully')
    console.log('')

    // Copy files to context directory
    log.info('Copying files to context directory...')
    const sourceContextDir = join(tempDir, CONTEXT_SOURCE_PATH)
    copyFiles(sourceContextDir, CONTEXT_DIR, verbose)
    log.success(`Files copied to: ${CONTEXT_DIR}`)
    console.log('')

    // Create manifest
    log.info('Creating manifest...')
    const manifestComponents: ManifestComponent[] = components.map((c) => ({
      id: c.id,
      name: c.name,
      path: c.path,
      local_path: join(CONTEXT_DIR, c.path.replace(`${CONTEXT_SOURCE_PATH}/`, '')),
      category: c.category,
    }))

    const manifest: Manifest = {
      version: '1.0.0',
      profile: customComponents.length > 0 ? 'custom' : profile,
      source: {
        repository: GITHUB_REPO,
        branch: GITHUB_BRANCH,
        commit: cloneResult.commit,
        downloaded_at: new Date().toISOString(),
      },
      context: manifestComponents,
    }

    writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2))
    log.success(`Manifest created: ${MANIFEST_FILE}`)
    console.log('')

    // Write .oac.json at project root so context-scout uses fast path on next session
    const projectRoot = dirname(PLUGIN_ROOT)
    const oacJsonPath = join(projectRoot, '.oac.json')
    const contextRelPath = relative(projectRoot, CONTEXT_DIR).replace(/\\/g, '/')
    if (!existsSync(oacJsonPath)) {
      const oacConfig = { version: '1', context: { root: contextRelPath } }
      writeFileSync(oacJsonPath, JSON.stringify(oacConfig, null, 2))
      log.success(`.oac.json created at project root → context.root = "${contextRelPath}"`)
    } else {
      log.info(`.oac.json already exists at project root — skipping (use --force to overwrite)`)
    }
    console.log('')

    // Clean up temp directory
    cleanup(tempDir, verbose)

    // Verify installation
    log.info('Verifying installation...')
    let filesExist = 0
    let filesMissing = 0

    for (const component of manifestComponents) {
      if (existsSync(component.local_path)) {
        filesExist++
        if (verbose) {
          log.success(`${component.id}: EXISTS`)
        }
      } else {
        filesMissing++
        log.error(`${component.id}: MISSING`)
      }
    }

    console.log('')
    log.success(`Installation complete!`)
    log.info(`Files verified: ${filesExist}/${manifestComponents.length}`)
    
    if (filesMissing > 0) {
      log.warning(`Missing files: ${filesMissing}`)
    }

    return {
      success: true,
      manifest: convertManifestToInstallManifest(manifest),
    }
  } catch (error) {
    log.error('Installation failed')
    log.error(error instanceof Error ? error.message : String(error))
    
    return {
      success: false,
      manifest: {
        version: '1.0.0',
        profile: 'essential',
        source: {
          repository: GITHUB_REPO,
          branch: GITHUB_BRANCH,
          commit: '',
          downloaded_at: new Date().toISOString(),
        },
        context: [],
      },
      errors: [error instanceof Error ? error.message : String(error)],
    }
  }
}

/**
 * Convert Manifest to InstallManifest format
 */
function convertManifestToInstallManifest(manifest: Manifest): InstallResult['manifest'] {
  return manifest as InstallResult['manifest']
}

/**
 * Show usage information
 */
function showUsage(): void {
  console.log(`
${colors.bold}Usage:${colors.reset} bun run install-context.ts [OPTIONS]

${colors.bold}OPTIONS:${colors.reset}
  --profile=PROFILE       Installation profile (default: essential)
                          Options: essential, standard, extended, specialized, all
  
  --component=ID          Install specific component by ID (can be used multiple times)
                          Example: --component=core-standards --component=openagents-repo
  
  --dry-run               Show what would be installed without downloading
  --force                 Force reinstall even if context exists
  --verbose               Show detailed output
  --help                  Show this help message

${colors.bold}PROFILES:${colors.reset}
  essential               Minimal components for basic functionality
  standard                Standard components for typical use
  extended                Extended components for advanced features
  specialized             Specialized components for specific domains
  all                     All available context

${colors.bold}EXAMPLES:${colors.reset}
  # Install essential profile (default)
  bun run install-context.ts

  # Install standard profile
  bun run install-context.ts --profile=standard

  # Install specific components
  bun run install-context.ts --component=core-standards --component=openagents-repo

  # Dry run to see what would be installed
  bun run install-context.ts --profile=extended --dry-run

  # Force reinstall
  bun run install-context.ts --force --verbose
`)
}

/**
 * CLI entry point
 */
async function main(): Promise<void> {
  const args = process.argv.slice(2)

  // Parse arguments
  let profile: Profile = 'essential'
  const customComponents: string[] = []
  let dryRun = false
  let force = false
  let verbose = false

  for (const arg of args) {
    if (arg === '--help' || arg === '-h') {
      showUsage()
      process.exit(0)
    } else if (arg === '--dry-run') {
      dryRun = true
    } else if (arg === '--force') {
      force = true
    } else if (arg === '--verbose' || arg === '-v') {
      verbose = true
    } else if (arg.startsWith('--profile=')) {
      profile = arg.split('=')[1] as Profile
    } else if (arg.startsWith('--component=')) {
      customComponents.push(arg.split('=')[1])
    } else {
      log.error(`Unknown argument: ${arg}`)
      showUsage()
      process.exit(1)
    }
  }

  // Run installation
  const result = await installContext({
    profile,
    customComponents,
    dryRun,
    force,
    verbose,
  })

  if (!result.success) {
    process.exit(1)
  }
}

// Run main function
main().catch((error) => {
  console.error('Fatal error:', error)
  process.exit(1)
})
