/**
 * Step: jail — Validate and provision the Warden control-plane jail.
 */
import { execSync } from 'child_process';
import fs from 'fs';

import {
  getControlPlaneBastillePlan,
  WARDEN_BRIDGE_NAME,
  WARDEN_CONTROL_PLANE_HOSTNAME,
  WARDEN_CONTROL_PLANE_IP,
  WARDEN_GATEWAY_IP,
} from '../src/jail-config.js';
import { logger } from '../src/logger.js';
import { commandExists, getPlatform } from './platform.js';
import { emitStatus } from './status.js';

const plan = getControlPlaneBastillePlan();
const BASTILLE_ROOT = '/usr/local/bastille';
const RELEASE_PATH = `${BASTILLE_ROOT}/releases/${plan.release}`;
const JAIL_ROOT_PATH = `${BASTILLE_ROOT}/jails/${plan.jailName}`;

type SetupAction = 'status' | 'plan' | 'create-control-plane';

function runCommand(command: string): string {
  return execSync(command, {
    encoding: 'utf-8',
    stdio: ['ignore', 'pipe', 'pipe'],
  }).trim();
}

function hasReleaseBootstrap(): boolean {
  return fs.existsSync(RELEASE_PATH);
}

function hasJailDirectory(): boolean {
  return fs.existsSync(JAIL_ROOT_PATH);
}

function getRunningJails(): string[] {
  try {
    const output = runCommand('jls -N name');
    return output
      .split('\n')
      .map((line) => line.trim())
      .filter(Boolean)
      .filter((line) => line !== 'name');
  } catch {
    return [];
  }
}

function getBridgeConfig(interfaceName: string): {
  exists: boolean;
  hasGateway: boolean;
  raw: string;
} {
  try {
    const raw = runCommand(`ifconfig ${interfaceName}`);
    return {
      exists: true,
      hasGateway: raw.includes(`inet ${WARDEN_GATEWAY_IP} `),
      raw,
    };
  } catch {
    return {
      exists: false,
      hasGateway: false,
      raw: '',
    };
  }
}

function parseAction(args: string[]): SetupAction {
  if (args.includes('--plan-control-plane')) return 'plan';
  if (args.includes('--create-control-plane')) return 'create-control-plane';
  return 'status';
}

function ensureFreebsd(): void {
  if (getPlatform() !== 'freebsd') {
    emitStatus('SETUP_JAIL', {
      PLATFORM: getPlatform(),
      STATUS: 'failed',
      ERROR: 'unsupported_platform',
      LOG: 'logs/setup.log',
    });
    process.exit(1);
  }
}

function emitPlanStatus(action: SetupAction): {
  hasBastille: boolean;
  hasJexec: boolean;
  hasJls: boolean;
  hasIfconfig: boolean;
  hasService: boolean;
  hasRelease: boolean;
  bridgeExists: boolean;
  bridgeHasGateway: boolean;
  jailExists: boolean;
  runningJails: string[];
  targetJailRunning: boolean;
} {
  const hasBastille = commandExists('bastille');
  const hasJexec = commandExists('jexec');
  const hasJls = commandExists('jls');
  const hasIfconfig = commandExists('ifconfig');
  const hasService = commandExists('service');
  const hasRelease = hasReleaseBootstrap();
  const bridge = getBridgeConfig(plan.bridge);
  const jailExists = hasJailDirectory();
  const runningJails = getRunningJails();
  const targetJailRunning = runningJails.includes(plan.jailName);

  emitStatus(
    action === 'create-control-plane'
      ? 'CREATE_CONTROL_PLANE'
      : action === 'plan'
        ? 'PLAN_CONTROL_PLANE'
        : 'SETUP_JAIL',
    {
      JAIL_NAME: plan.jailName,
      HOSTNAME: plan.hostname,
      RELEASE: plan.release,
      BRIDGE: plan.bridge,
      GATEWAY: plan.gateway,
      JAIL_IP: plan.jailIp,
      JEXEC: hasJexec,
      JLS: hasJls,
      IFCONFIG: hasIfconfig,
      BASTILLE: hasBastille,
      SERVICE: hasService,
      RELEASE_BOOTSTRAPPED: hasRelease,
      BRIDGE_EXISTS: bridge.exists,
      BRIDGE_HAS_GATEWAY: bridge.hasGateway,
      JAIL_EXISTS: jailExists,
      RUNNING_JAILS: runningJails.join(',') || 'none',
      TARGET_JAIL_RUNNING: targetJailRunning,
      CREATE_CMD: `bastille ${plan.createArgs.join(' ')}`,
      START_CMD: `bastille ${plan.startArgs.join(' ')}`,
      STATUS:
        hasBastille &&
        hasJexec &&
        hasJls &&
        hasIfconfig &&
        hasService &&
        hasRelease &&
        bridge.exists &&
        bridge.hasGateway
          ? 'success'
          : 'failed',
      LOG: 'logs/setup.log',
    },
  );

  return {
    hasBastille,
    hasJexec,
    hasJls,
    hasIfconfig,
    hasService,
    hasRelease,
    bridgeExists: bridge.exists,
    bridgeHasGateway: bridge.hasGateway,
    jailExists,
    runningJails,
    targetJailRunning,
  };
}

function ensureProvisioningReady(status: ReturnType<typeof emitPlanStatus>): void {
  const failures: string[] = [];

  if (!status.hasBastille) failures.push('bastille_not_installed');
  if (!status.hasJexec) failures.push('missing_jexec');
  if (!status.hasJls) failures.push('missing_jls');
  if (!status.hasIfconfig) failures.push('missing_ifconfig');
  if (!status.hasService) failures.push('missing_service');
  if (!status.hasRelease) failures.push('release_not_bootstrapped');
  if (!status.bridgeExists) failures.push('missing_warden0_bridge');
  if (!status.bridgeHasGateway) failures.push('bridge_missing_gateway_192.168.100.1');

  if (failures.length > 0) {
    throw new Error(failures.join(', '));
  }
}

function ensureHostnameInsideJail(jailName: string, hostname: string): void {
  execSync(`bastille config ${jailName} set host.hostname ${hostname}`, {
    stdio: 'inherit',
  });
}

function ensureJailRunning(jailName: string): void {
  execSync(`bastille start ${jailName}`, { stdio: 'inherit' });
}

function restartJail(jailName: string): void {
  execSync(`bastille restart ${jailName}`, { stdio: 'inherit' });
}

function createControlPlane(): void {
  const status = emitPlanStatus('create-control-plane');
  ensureProvisioningReady(status);

  if (!status.jailExists) {
    logger.info(
      {
        jailName: plan.jailName,
        release: plan.release,
        bridge: plan.bridge,
        jailIp: plan.jailIp,
      },
      'Creating Warden control-plane jail',
    );
    execSync(`bastille ${plan.createArgs.join(' ')}`, { stdio: 'inherit' });
  }

  ensureJailRunning(plan.jailName);
  ensureHostnameInsideJail(plan.jailName, plan.hostname);
  restartJail(plan.jailName);

  emitStatus('CREATE_CONTROL_PLANE', {
    JAIL_NAME: plan.jailName,
    HOSTNAME: plan.hostname,
    RELEASE: plan.release,
    BRIDGE: plan.bridge,
    GATEWAY: plan.gateway,
    JAIL_IP: plan.jailIp,
    STATUS: 'success',
    LOG: 'logs/setup.log',
  });
}

export async function run(args: string[]): Promise<void> {
  logger.info('Starting jail setup');
  ensureFreebsd();

  const action = parseAction(args);

  if (action === 'plan') {
    const status = emitPlanStatus(action);
    if (
      !status.hasBastille ||
      !status.hasRelease ||
      !status.bridgeExists ||
      !status.bridgeHasGateway
    ) {
      process.exit(1);
    }
    return;
  }

  if (action === 'create-control-plane') {
    createControlPlane();
    return;
  }

  const status = emitPlanStatus(action);
  if (
    !status.hasBastille ||
    !status.hasJexec ||
    !status.hasJls ||
    !status.hasIfconfig ||
    !status.hasService
  ) {
    process.exit(1);
  }
}

export const __testables = {
  parseAction,
  getBridgeConfig,
};
