#!/usr/bin/env bun
import { initCommand, listTemplates } from './commands/init.js';
import { fingerprintCommand } from './commands/fingerprint.js';
import { publishCommand } from './commands/publish.js';
import { rotateCommand } from './commands/rotate.js';
import {
  peerAddCommand,
  peerListCommand,
  peerVerifyCommand,
  peerRemoveCommand,
} from './commands/peer.js';
import { dashboardCommand } from './commands/dashboard.js';
import { doctorCommand } from './commands/doctor.js';
import { backupExportCommand, backupRestoreCommand } from './commands/backup.js';
import {
  migrateStorageCommand,
  rotateStorageKeyCommand,
  parseStorageArgs,
} from './commands/storage.js';

const VERSION = '0.1.0';

const HELP = `
Shade CLI v${VERSION}

Usage: shade <command> [args]

Commands:
  init [name]              Scaffold a new Shade project
    --template <name>        Template to use (default: bun-server)
    --prekey-server <url>    Override prekey server URL
    --doctor                 Run shade doctor against the new project
  fingerprint [address]    Print your own or a peer's fingerprint
  publish                  Re-upload your bundle to the prekey server
  rotate                   Rotate the signed prekey
    --identity               Rotate the full identity (destructive)
  peer add <address>       Establish a session with a peer
  peer list                List active sessions
  peer verify <address> <fingerprint>
                           Check a peer's fingerprint matches
  peer remove <address>    Delete a session
  dashboard                Open the observer dashboard in the browser
  doctor                   Diagnose setup issues
  backup export <file>     Export an encrypted backup (prompts for passphrase)
  backup restore <file>    Restore from a backup file
  migrate-storage          Encrypt the local SQLite store at-rest (V3.2)
    --key-source <kind>      passphrase | keychain | injected
    --passphrase <s>         passphrase for KDF (or env SHADE_STORAGE_PASSPHRASE)
    --keychain-service <s>   keychain service name (default: shade.storage)
    --keychain-account <s>   keychain account name (default: default)
    --key-hex <hex>          inject a raw 32-byte key as hex
    --salt-file <path>       passphrase salt file (default: <db>.salt)
    --dry-run                validate + report without writing
    --keep-original          keep unencrypted tables after migration
    --no-backup              skip the .bak snapshot
  rotate-storage-key       Re-key the encrypted SQLite store (V3.2)
    --key-source / --passphrase / …  (current key — same flags as migrate)
    --new-key-source / --new-passphrase / …  (target key)
  help                     Show this message

Config:
  Reads .shaderc.json from cwd, or env vars:
    SHADE_PREKEY_SERVER, SHADE_DB_PATH, SHADE_OBSERVER_TOKEN,
    SHADE_OBSERVER_URL, SHADE_ADDRESS
`;

async function main(): Promise<void> {
  const args = process.argv.slice(2);
  const cmd = args[0];

  try {
    switch (cmd) {
      case 'init': {
        const options = parseInitArgs(args.slice(1));
        await initCommand(options);
        break;
      }
      case 'fingerprint':
        await fingerprintCommand(args[1]);
        break;
      case 'publish':
        await publishCommand();
        break;
      case 'rotate':
        await rotateCommand({ identity: args.includes('--identity') });
        break;
      case 'peer': {
        const sub = args[1];
        if (sub === 'add') await peerAddCommand(requireArg(args[2], 'address'));
        else if (sub === 'list') await peerListCommand();
        else if (sub === 'verify')
          await peerVerifyCommand(
            requireArg(args[2], 'address'),
            args.slice(3).join(' '),
          );
        else if (sub === 'remove') await peerRemoveCommand(requireArg(args[2], 'address'));
        else {
          console.error(`Unknown peer subcommand: ${sub}`);
          process.exit(1);
        }
        break;
      }
      case 'dashboard':
        await dashboardCommand();
        break;
      case 'doctor':
        await doctorCommand();
        break;
      case 'backup': {
        const sub = args[1];
        const file = args[2];
        if (sub === 'export') await backupExportCommand(requireArg(file, 'file'));
        else if (sub === 'restore') await backupRestoreCommand(requireArg(file, 'file'));
        else {
          console.error(`Unknown backup subcommand: ${sub}. Use export or restore.`);
          process.exit(1);
        }
        break;
      }
      case 'migrate-storage': {
        const opts = parseStorageArgs(args.slice(1));
        await migrateStorageCommand(opts);
        break;
      }
      case 'rotate-storage-key': {
        const opts = parseStorageArgs(args.slice(1));
        await rotateStorageKeyCommand(opts);
        break;
      }
      case 'help':
      case '--help':
      case '-h':
      case undefined:
        console.log(HELP);
        console.log('\nAvailable templates:');
        for (const name of listTemplates()) console.log(`  ${name}`);
        break;
      case '--version':
      case '-v':
        console.log(VERSION);
        break;
      default:
        console.error(`Unknown command: ${cmd}`);
        console.log(HELP);
        process.exit(1);
    }
  } catch (err) {
    console.error(`\x1b[31mError:\x1b[0m ${(err as Error).message}`);
    process.exit(1);
  }
}

function parseInitArgs(args: string[]): {
  name?: string;
  template?: string;
  prekeyServer?: string;
  runDoctor?: boolean;
} {
  const options: ReturnType<typeof parseInitArgs> = {};
  for (let i = 0; i < args.length; i++) {
    if (args[i] === '--template') options.template = args[++i];
    else if (args[i] === '--prekey-server') options.prekeyServer = args[++i];
    else if (args[i] === '--doctor') options.runDoctor = true;
    else if (!args[i]!.startsWith('--')) options.name = args[i];
  }
  return options;
}

function requireArg(arg: string | undefined, name: string): string {
  if (!arg) {
    console.error(`Missing required argument: ${name}`);
    process.exit(1);
  }
  return arg;
}

main();
