import { spawn, execSync } from 'node:child_process';
import { createConnection } from 'node:net';
import { resolve, dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { mkdirSync, writeFileSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';

const __dirname = dirname(fileURLToPath(import.meta.url));
const projectRoot = resolve(__dirname, '../..');

// Debug binary path (built with `cargo tauri build --debug --no-bundle`)
// Cargo workspace target dir is at v2/target/, not v2/src-tauri/target/
const tauriBinary = resolve(projectRoot, 'target/debug/agent-orchestrator');

let tauriDriver;

// ── Test Fixture (created eagerly so env vars are available for capabilities) ──
const fixtureRoot = join(tmpdir(), `agor-e2e-${Date.now()}`);
const fixtureDataDir = join(fixtureRoot, 'data');
const fixtureConfigDir = join(fixtureRoot, 'config');
const fixtureProjectDir = join(fixtureRoot, 'test-project');

mkdirSync(fixtureDataDir, { recursive: true });
mkdirSync(fixtureConfigDir, { recursive: true });
mkdirSync(fixtureProjectDir, { recursive: true });

// Create a minimal git repo for agent testing
execSync('git init', { cwd: fixtureProjectDir, stdio: 'ignore' });
execSync('git config user.email "test@agor.dev"', { cwd: fixtureProjectDir, stdio: 'ignore' });
execSync('git config user.name "Agor Test"', { cwd: fixtureProjectDir, stdio: 'ignore' });
writeFileSync(join(fixtureProjectDir, 'README.md'), '# Test Project\n\nA simple test project for Agor E2E tests.\n');
writeFileSync(join(fixtureProjectDir, 'hello.py'), 'def greet(name: str) -> str:\n    return f"Hello, {name}!"\n');
execSync('git add -A && git commit -m "initial commit"', { cwd: fixtureProjectDir, stdio: 'ignore' });

// Write groups.json with one group containing the test project
writeFileSync(
  join(fixtureConfigDir, 'groups.json'),
  JSON.stringify({
    version: 1,
    groups: [{
      id: 'test-group',
      name: 'Test Group',
      projects: [{
        id: 'test-project',
        name: 'Test Project',
        identifier: 'test-project',
        description: 'E2E test project',
        icon: '\uf120',
        cwd: fixtureProjectDir,
        profile: 'default',
        enabled: true,
      }],
      agents: [],
    }],
    activeGroupId: 'test-group',
  }, null, 2),
);

// Inject env vars into process.env so tauri-driver inherits them
// (tauri:options.env may not reliably set process-level env vars)
process.env.AGOR_TEST = '1';
process.env.AGOR_TEST_DATA_DIR = fixtureDataDir;
process.env.AGOR_TEST_CONFIG_DIR = fixtureConfigDir;

console.log(`Test fixture created at ${fixtureRoot}`);

export const config = {
  // ── Runner ──
  runner: 'local',
  maxInstances: 1, // Tauri doesn't support parallel sessions

  // ── Connection (external tauri-driver on port 4444) ──
  hostname: 'localhost',
  port: 4444,
  path: '/',

  // ── Specs ──
  // Single spec file — Tauri launches one app instance per session,
  // and tauri-driver can't re-create sessions between spec files.
  specs: [
    resolve(__dirname, 'specs/agor.test.ts'),
    resolve(__dirname, 'specs/agent-scenarios.test.ts'),
    resolve(__dirname, 'specs/phase-b.test.ts'),
    resolve(__dirname, 'specs/phase-c.test.ts'),
  ],

  // ── Capabilities ──
  capabilities: [{
    // Disable BiDi negotiation — tauri-driver doesn't support webSocketUrl
    'wdio:enforceWebDriverClassic': true,
    'tauri:options': {
      application: tauriBinary,
      // Test isolation: fixture-created data/config dirs, disable watchers/telemetry
      env: {
        AGOR_TEST: '1',
        AGOR_TEST_DATA_DIR: fixtureDataDir,
        AGOR_TEST_CONFIG_DIR: fixtureConfigDir,
      },
    },
  }],

  // ── Framework ──
  framework: 'mocha',
  mochaOpts: {
    ui: 'bdd',
    timeout: 180_000,
  },

  // ── Reporter ──
  reporters: ['spec'],

  // ── Logging ──
  logLevel: 'warn',

  // ── Timeouts ──
  waitforTimeout: 10_000,
  connectionRetryTimeout: 30_000,
  connectionRetryCount: 3,

  // ── Hooks ──

  /**
   * Build the debug binary before the test run.
   * Uses --debug --no-bundle for fastest build time.
   */
  onPrepare() {
    if (process.env.SKIP_BUILD) {
      console.log('SKIP_BUILD set — using existing debug binary.');
      return Promise.resolve();
    }
    return new Promise((resolve, reject) => {
      console.log('Building Tauri debug binary...');
      const build = spawn('cargo', ['tauri', 'build', '--debug', '--no-bundle'], {
        cwd: projectRoot,
        stdio: 'inherit',
      });
      build.on('close', (code) => {
        if (code === 0) {
          console.log('Debug binary ready.');
          resolve();
        } else {
          reject(new Error(`Tauri build failed with exit code ${code}`));
        }
      });
      build.on('error', reject);
    });
  },

  /**
   * Spawn tauri-driver before the session.
   * tauri-driver bridges WebDriver protocol to WebKit2GTK's inspector.
   * Uses TCP probe to confirm port 4444 is accepting connections.
   */
  beforeSession() {
    return new Promise((res, reject) => {
      tauriDriver = spawn('tauri-driver', [], {
        stdio: ['ignore', 'pipe', 'pipe'],
      });

      tauriDriver.on('error', (err) => {
        reject(new Error(
          `Failed to start tauri-driver: ${err.message}. ` +
          'Install it with: cargo install tauri-driver'
        ));
      });

      // TCP readiness probe — poll port 4444 until it accepts a connection
      const maxWaitMs = 10_000;
      const intervalMs = 200;
      const deadline = Date.now() + maxWaitMs;

      function probe() {
        if (Date.now() > deadline) {
          reject(new Error('tauri-driver did not become ready within 10s'));
          return;
        }
        const sock = createConnection({ port: 4444, host: 'localhost' }, () => {
          sock.destroy();
          res();
        });
        sock.on('error', () => {
          sock.destroy();
          setTimeout(probe, intervalMs);
        });
      }

      // Give it a moment before first probe
      setTimeout(probe, 300);
    });
  },

  /**
   * Kill tauri-driver after the test run.
   */
  afterSession() {
    if (tauriDriver) {
      tauriDriver.kill();
      tauriDriver = null;
    }
    // Clean up test fixture
    try {
      rmSync(fixtureRoot, { recursive: true, force: true });
      console.log('Test fixture cleaned up.');
    } catch { /* best-effort cleanup */ }
  },

  // ── TypeScript (auto-compile via tsx) ──
  autoCompileOpts: {
    tsNodeOpts: {
      project: resolve(__dirname, 'tsconfig.json'),
    },
  },
};
