import { Command } from 'commander';
import type { ApiClient } from '../api-client.js';

export interface MigrateCommandDeps {
  client: ApiClient;
  log: (...args: unknown[]) => void;
}

interface MigrateResult {
  migrated: Array<{ name: string }>;
  skipped: Array<{ name: string; reason: string }>;
  failed: Array<{ name: string; error: string }>;
}

interface DryRunResult {
  dryRun: true;
  candidates: Array<{ id: string; name: string }>;
}

/**
 * Top-level `mcpctl migrate <subcommand>` verb.
 *
 * Today only `secrets` is implemented (SecretBackend → SecretBackend move),
 * but the command is structured so new migrations can slot in.
 *
 * Per-secret atomicity is handled server-side — if this command is interrupted
 * mid-run, re-running is idempotent (skips secrets already on the destination).
 */
export function createMigrateCommand(deps: MigrateCommandDeps): Command {
  const { client, log } = deps;

  const cmd = new Command('migrate')
    .description('Move resources between backends (currently: secrets between SecretBackends)');

  cmd.command('secrets')
    .description('Migrate secrets from one SecretBackend to another')
    .requiredOption('--from <name>', 'Source SecretBackend name')
    .requiredOption('--to <name>', 'Destination SecretBackend name')
    .option('--names <csv>', 'Comma-separated secret names (default: all)')
    .option('--keep-source', 'Leave the source copy intact (default: delete from source after write+commit)')
    .option('--dry-run', 'Show which secrets would be migrated without touching them')
    .action(async (opts) => {
      const body: Record<string, unknown> = { from: opts.from, to: opts.to };
      if (opts.names) body.names = (opts.names as string).split(',').map((s) => s.trim()).filter(Boolean);
      if (opts.keepSource) body.keepSource = true;
      if (opts.dryRun) body.dryRun = true;

      if (opts.dryRun) {
        const res = await client.post<DryRunResult>('/api/v1/secrets/migrate', body);
        if (res.candidates.length === 0) {
          log(`No secrets to migrate from '${opts.from as string}' to '${opts.to as string}'.`);
          return;
        }
        log(`Dry run — ${String(res.candidates.length)} secret(s) would be migrated from '${opts.from as string}' → '${opts.to as string}':`);
        for (const c of res.candidates) log(`  - ${c.name}`);
        return;
      }

      const res = await client.post<MigrateResult>('/api/v1/secrets/migrate', body);

      if (res.migrated.length > 0) {
        log(`Migrated ${String(res.migrated.length)} secret(s) from '${opts.from as string}' → '${opts.to as string}':`);
        for (const m of res.migrated) log(`  ✓ ${m.name}`);
      }
      if (res.skipped.length > 0) {
        log(`Skipped ${String(res.skipped.length)}:`);
        for (const s of res.skipped) log(`  - ${s.name}: ${s.reason}`);
      }
      if (res.failed.length > 0) {
        log(`Failed ${String(res.failed.length)}:`);
        for (const f of res.failed) log(`  ✗ ${f.name}: ${f.error}`);
        process.exitCode = 1;
      }
      if (res.migrated.length === 0 && res.skipped.length === 0 && res.failed.length === 0) {
        log(`No secrets to migrate from '${opts.from as string}' to '${opts.to as string}'.`);
      }
    });

  return cmd;
}
