import { spawn, ChildProcess } from 'child_process';
import { join } from 'path';

export interface ApiServerHarnessOptions {
  port?: number;
  env?: Record<string, string>;
}

export class ApiServerHarness {
  private process: ChildProcess | null = null;
  private logs: string[] = [];
  private port: number;

  constructor(options: ApiServerHarnessOptions = {}) {
    this.port = options.port || 3001;
  }

  async start(): Promise<void> {
    return new Promise((resolve, reject) => {
      const cwd = join(process.cwd(), 'apps/api');
      
      this.process = spawn('npm', ['run', 'start:dev'], {
        cwd,
        env: {
          ...process.env,
          PORT: this.port.toString(),
          GRIDPILOT_API_PERSISTENCE: 'inmemory',
          ENABLE_BOOTSTRAP: 'true',
        },
        shell: true,
        detached: true,
      });

      let resolved = false;

      const checkReadiness = async () => {
        if (resolved) return;
        try {
          const res = await fetch(`http://localhost:${this.port}/health`);
          if (res.ok) {
            resolved = true;
            resolve();
          }
        } catch (e) {
          // Not ready yet
        }
      };

      this.process.stdout?.on('data', (data) => {
        const str = data.toString();
        this.logs.push(str);
        if (str.includes('Nest application successfully started') || str.includes('started')) {
          checkReadiness();
        }
      });

      this.process.stderr?.on('data', (data) => {
        const str = data.toString();
        this.logs.push(str);
      });

      this.process.on('error', (err) => {
        if (!resolved) {
          resolved = true;
          reject(err);
        }
      });

      this.process.on('exit', (code) => {
        if (!resolved && code !== 0 && code !== null) {
          resolved = true;
          reject(new Error(`API server exited with code ${code}`));
        }
      });

      // Timeout after 60 seconds
      setTimeout(() => {
        if (!resolved) {
          resolved = true;
          reject(new Error(`API server failed to start within 60s. Logs:\n${this.getLogTail(20)}`));
        }
      }, 60000);
    });
  }

  async stop(): Promise<void> {
    if (this.process && this.process.pid) {
      try {
        process.kill(-this.process.pid);
      } catch (e) {
        this.process.kill();
      }
      this.process = null;
    }
  }

  getLogTail(lines: number = 60): string {
    return this.logs.slice(-lines).join('');
  }
}
