/**
 * Alias command -- create top-level shim binaries that expand to `agents <subcommand>`.
 *
 * Generates executable scripts in ~/.agents/shims/ (already on PATH from the
 * standard agents-cli install). Running `teams` becomes equivalent to
 * `agents teams`. Aliases are tracked in ~/.agents/aliases.json so we can
 * list, regenerate, or remove them without scanning the shims directory
 * (which also holds agent CLI shims like claude/codex/gemini).
 */
import type { Command } from 'commander';
import chalk from 'chalk';
import * as fs from 'fs';
import * as path from 'path';

import { getUserAgentsDir } from '../lib/state.js';
import { getShimsDir } from '../lib/shims.js';
import { ALL_AGENT_IDS, AGENTS } from '../lib/agents.js';

const ALIASES_FILE = path.join(getUserAgentsDir(), 'aliases.json');

interface AliasMap { [name: string]: string }

/** Names that would clobber an agent CLI shim or the `agents` binary itself. */
function reservedNames(): Set<string> {
  const reserved = new Set<string>(['agents']);
  for (const id of ALL_AGENT_IDS) reserved.add(AGENTS[id].cliCommand);
  return reserved;
}

function readAliases(): AliasMap {
  if (!fs.existsSync(ALIASES_FILE)) return {};
  try {
    const parsed = JSON.parse(fs.readFileSync(ALIASES_FILE, 'utf-8'));
    return parsed && typeof parsed === 'object' ? parsed as AliasMap : {};
  } catch {
    return {};
  }
}

function writeAliases(map: AliasMap): void {
  fs.mkdirSync(path.dirname(ALIASES_FILE), { recursive: true });
  fs.writeFileSync(ALIASES_FILE, JSON.stringify(map, null, 2) + '\n', 'utf-8');
}

function shimPath(name: string): string {
  return path.join(getShimsDir(), name);
}

function generateAliasShim(name: string, expansion: string): string {
  return `#!/bin/bash
# Auto-generated by agents-cli - do not edit
# Alias shim: ${name} -> agents ${expansion}
exec agents ${expansion} "$@"
`;
}

function isAliasShim(filePath: string): boolean {
  try {
    const head = fs.readFileSync(filePath, 'utf-8').slice(0, 200);
    return head.includes('# Alias shim:');
  } catch {
    return false;
  }
}

export function registerAliasCommand(program: Command): void {
  const cmd = program
    .command('alias')
    .description('Create shorthand binaries that expand to `agents <subcommand>`');

  cmd
    .command('add <name> [expansion...]')
    .description('Create an alias shim. If expansion is omitted, defaults to <name>.')
    .action((name: string, expansion: string[]) => {
      const expanded = expansion.length > 0 ? expansion.join(' ') : name;

      if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name)) {
        console.error(chalk.red(`Invalid alias name "${name}". Use letters, digits, _ or -.`));
        process.exit(1);
      }
      if (reservedNames().has(name)) {
        console.error(chalk.red(`"${name}" is reserved (collides with an agent CLI or the agents binary).`));
        process.exit(1);
      }

      const target = shimPath(name);
      if (fs.existsSync(target) && !isAliasShim(target)) {
        console.error(chalk.red(`Refusing to overwrite ${target} -- not an agents-cli alias shim.`));
        process.exit(1);
      }

      fs.mkdirSync(getShimsDir(), { recursive: true });
      fs.writeFileSync(target, generateAliasShim(name, expanded), 'utf-8');
      fs.chmodSync(target, 0o755);

      const map = readAliases();
      map[name] = expanded;
      writeAliases(map);

      console.log(`${chalk.green('Created')} alias ${chalk.bold(name)} -> ${chalk.dim(`agents ${expanded}`)}`);
    });

  cmd
    .command('remove <name>')
    .alias('rm')
    .description('Delete an alias shim')
    .action((name: string) => {
      const map = readAliases();
      const target = shimPath(name);

      if (!(name in map) && !fs.existsSync(target)) {
        console.error(chalk.red(`No alias named "${name}".`));
        process.exit(1);
      }

      if (fs.existsSync(target)) {
        if (!isAliasShim(target)) {
          console.error(chalk.red(`Refusing to delete ${target} -- not an agents-cli alias shim.`));
          process.exit(1);
        }
        fs.unlinkSync(target);
      }

      delete map[name];
      writeAliases(map);
      console.log(`${chalk.yellow('Removed')} alias ${chalk.bold(name)}`);
    });

  cmd
    .command('list')
    .alias('ls')
    .description('Show installed aliases')
    .action(() => {
      const map = readAliases();
      const entries = Object.entries(map);

      if (entries.length === 0) {
        console.log(chalk.gray('No aliases. Try: agents alias add teams'));
        return;
      }

      const width = entries.reduce((max, [n]) => Math.max(max, n.length), 0);
      for (const [name, expansion] of entries) {
        console.log(`  ${chalk.bold(name.padEnd(width))}  ${chalk.dim('->')} agents ${expansion}`);
      }
    });
}
