import { execSync } from 'child_process';
import * as fs from 'fs';

export interface JailInfo {
  name: string;
  jid: number;
  hostname: string;
  path: string;
  ip: string;
}

export class JailRuntime {
  private static instance: JailRuntime;
  private inJail: boolean;
  private jailInfo: JailInfo | null = null;

  private constructor() {
    this.inJail = this.detectJail();
    if (this.inJail) {
      this.jailInfo = this.loadJailInfo();
    }
  }

  static getInstance(): JailRuntime {
    if (!JailRuntime.instance) {
      JailRuntime.instance = new JailRuntime();
    }
    return JailRuntime.instance;
  }

  private detectJail(): boolean {
    try {
      const result = execSync('sysctl -n security.jail.jailed 2>/dev/null', {
        encoding: 'utf-8',
      });
      return result.trim() === '1';
    } catch {
      return false;
    }
  }

  private loadJailInfo(): JailInfo | null {
    try {
      const jlsOutput = execSync('jls -n', { encoding: 'utf-8' });
      const lines = jlsOutput.split('\n');

      for (const line of lines) {
        if (line.includes('clawdie')) {
          const parts = line.trim().split(/\s+/);
          const info: Record<string, string> = {};

          for (const part of parts) {
            const [key, value] = part.split('=');
            if (key && value) {
              info[key] = value;
            }
          }

          return {
            name: info.name || 'unknown',
            jid: parseInt(info.jid || '0', 10),
            hostname: info['host.hostname'] || 'unknown',
            path: info.path || '/jails/clawdie',
            ip: info['ip4.addr'] || '0.0.0.0',
          };
        }
      }
    } catch (error) {
      console.error('Failed to get jail info:', error);
    }

    return null;
  }

  isInJail(): boolean {
    return this.inJail;
  }

  getJailInfo(): JailInfo | null {
    return this.jailInfo;
  }

  static isLinuxEmulationEnabled(): boolean {
    try {
      const result = execSync('sysrc -n linux_enable 2>/dev/null', {
        encoding: 'utf-8',
      });
      return result.trim() === 'YES';
    } catch {
      return false;
    }
  }

  static getJailPath(): string {
    return process.env.JAIL_PATH || '/jails/clawdie';
  }

  static async spawnInJail(
    jailName: string,
    command: string,
    args: string[] = [],
  ): Promise<{ stdout: string; stderr: string; exitCode: number }> {
    return new Promise((resolve, reject) => {
      try {
        const fullCommand = `jexec ${jailName} ${command} ${args.join(' ')}`;
        const stdout = execSync(fullCommand, {
          encoding: 'utf-8',
          stdio: ['pipe', 'pipe', 'pipe'],
        });

        resolve({
          stdout,
          stderr: '',
          exitCode: 0,
        });
      } catch (error: any) {
        reject(new Error(`Failed to spawn process in jail: ${error.message}`));
      }
    });
  }

  static validateJailEnvironment(): {
    valid: boolean;
    errors: string[];
    warnings: string[];
  } {
    const errors: string[] = [];
    const warnings: string[] = [];

    if (this.isLinuxEmulationEnabled()) {
      warnings.push(
        'Linux emulation is enabled. Disable it for native performance.',
      );
    }

    const jailPath = this.getJailPath();
    if (!fs.existsSync(jailPath)) {
      errors.push(`Jail path ${jailPath} does not exist.`);
    }

    const instance = this.getInstance();
    if (!instance.isInJail()) {
      warnings.push(
        'Not running inside a jail. Jail isolation is recommended.',
      );
    }

    return {
      valid: errors.length === 0,
      errors,
      warnings,
    };
  }
}

export function useJailRuntime(): boolean {
  const platform = process.platform;

  if (platform === 'freebsd') {
    const validation = JailRuntime.validateJailEnvironment();
    if (validation.valid) {
      return true;
    }
    if (validation.errors.length > 0) {
      console.error('Jail validation errors:', validation.errors);
    }
    if (validation.warnings.length > 0) {
      console.warn('Jail validation warnings:', validation.warnings);
    }
  }

  return false;
}
