import * as path from 'path';
import * as fs from 'fs';

export interface RuntimeStatus {
  platform: string;
  arch: string;
  nodeVersion: string;
  nativeBootstrapAvailable: boolean;
  nativeBootstrapPath?: string;
  manifestPath?: string;
  bootstrapChecksum?: string;
}

export interface WindowOptions {
  width?: number;
  height?: number;
  showDevTools?: boolean;
  backgroundColor?: string;
}

export interface OrbitConfig {
  appName: string;
  entryFile: string;
  preloadFile?: string;
  userDataDir?: string;
  windows?: WindowOptions;
}

export interface LaunchPlan {
  executable: string;
  args: string[];
  cwd: string;
  env: Record<string, string>;
  config: OrbitConfig;
}

interface BootstrapManifest {
  version?: string;
  platform?: string;
  arch?: string;
  formatter?: string;
  checksum?: string;
  timestamp?: string;
}

function normalizePlatform(): string {
  if (process.platform === 'win32') {
    return 'windows';
  }

  if (process.platform === 'darwin') {
    return 'macos';
  }

  return 'linux';
}

function normalizeArch(): string {
  if (process.arch === 'x64') {
    return 'amd64';
  }

  if (process.arch === 'arm64') {
    return 'arm64';
  }

  return process.arch;
}

export class OrbitRuntime {
  private readonly binaryDir: string;
  private readonly manifestPath: string;

  constructor() {
    this.binaryDir = path.join(__dirname, '..', 'bin', 'formatters');
    this.manifestPath = path.join(this.binaryDir, 'manifest.json');
  }

  private getNativeBootstrapPath(): string {
    const platform = normalizePlatform();
    const arch = normalizeArch();

    const ext =
      platform === 'windows'
        ? '.exe'
        : platform === 'macos'
          ? '.macos'
          : '.elf';

    return path.join(this.binaryDir, `formatter-general-${arch}${ext}`);
  }

  private readManifest(): BootstrapManifest | undefined {
    if (!fs.existsSync(this.manifestPath)) {
      return undefined;
    }

    try {
      const content = fs.readFileSync(this.manifestPath, 'utf-8');
      return JSON.parse(content) as BootstrapManifest;
    } catch {
      return undefined;
    }
  }

  getRuntimeStatus(): RuntimeStatus {
    const nativeBootstrapPath = this.getNativeBootstrapPath();
    const manifest = this.readManifest();
    const hasBootstrap = fs.existsSync(nativeBootstrapPath);

    return {
      platform: normalizePlatform(),
      arch: normalizeArch(),
      nodeVersion: process.version,
      nativeBootstrapAvailable: hasBootstrap,
      nativeBootstrapPath: hasBootstrap ? nativeBootstrapPath : undefined,
      manifestPath: manifest ? this.manifestPath : undefined,
      bootstrapChecksum: manifest?.checksum,
    };
  }

  createLaunchPlan(config: OrbitConfig): LaunchPlan {
    const status = this.getRuntimeStatus();
    const entryFile = path.resolve(config.entryFile);
    const preloadFile = config.preloadFile
      ? path.resolve(config.preloadFile)
      : undefined;

    const args = [entryFile];
    if (preloadFile) {
      args.push('--require', preloadFile);
    }

    if (config.windows?.showDevTools) {
      args.push('--inspect');
    }

    const env: Record<string, string> = {
      ...Object.keys(process.env).reduce<Record<string, string>>((acc, key) => {
        const value = process.env[key];
        if (typeof value === 'string') {
          acc[key] = value;
        }
        return acc;
      }, {}),
    };

    env.ELECTRON_ORBIT_APP = config.appName;
    env.ELECTRON_ORBIT_PLATFORM = status.platform;
    env.ELECTRON_ORBIT_ARCH = status.arch;

    if (config.userDataDir) {
      env.ELECTRON_ORBIT_USER_DATA = path.resolve(config.userDataDir);
    }

    return {
      executable: process.execPath,
      args,
      cwd: path.dirname(entryFile),
      env,
      config,
    };
  }
}

export function createOrbitRuntime(): OrbitRuntime {
  return new OrbitRuntime();
}
