import { spawn, type ChildProcess } from 'node:child_process';

export class CloudflareTunnel {
  private process: ChildProcess | null = null;
  private url: string | null = null;

  async start(port: number): Promise<string> {
    return new Promise((resolve, reject) => {
      const timeout = setTimeout(() => {
        reject(new Error('Tunnel startup timed out after 30s'));
      }, 30000);

      this.process = spawn('cloudflared', ['tunnel', '--url', `http://localhost:${port}`], {
        stdio: ['ignore', 'pipe', 'pipe'],
      });

      const handleOutput = (data: Buffer) => {
        const text = data.toString();
        // cloudflared prints the URL to stderr
        const urlMatch = text.match(/https:\/\/[a-z0-9-]+\.trycloudflare\.com/);
        if (urlMatch && !this.url) {
          this.url = urlMatch[0];
          clearTimeout(timeout);
          resolve(this.url);
        }
      };

      this.process.stdout?.on('data', handleOutput);
      this.process.stderr?.on('data', handleOutput);

      this.process.on('error', (err) => {
        clearTimeout(timeout);
        if (err.message.includes('ENOENT')) {
          reject(
            new Error(
              'cloudflared not found. Install with: brew install cloudflared\n' +
                'Or disable tunnel with: --no-tunnel'
            )
          );
        } else {
          reject(err);
        }
      });

      this.process.on('exit', (code) => {
        if (!this.url) {
          clearTimeout(timeout);
          reject(new Error(`cloudflared exited with code ${code}`));
        }
      });
    });
  }

  getUrl(): string | null {
    return this.url;
  }

  stop(): void {
    if (this.process) {
      this.process.kill('SIGTERM');
      this.process = null;
      this.url = null;
    }
  }
}
