import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { EventEmitter } from 'events';

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

let tempClaudeRoot = '';
let tempTeamsBase = '';

vi.mock('@main/utils/pathDecoder', async (importOriginal) => {
  const actual = await importOriginal<typeof import('@main/utils/pathDecoder')>();
  return {
    ...actual,
    getAutoDetectedClaudeBasePath: () => tempClaudeRoot,
    getClaudeBasePath: () => tempClaudeRoot,
    getTeamsBasePath: () => tempTeamsBase,
  };
});

import { TeamProvisioningService } from '@main/services/team/TeamProvisioningService';

describe('TeamProvisioningService idempotent launch guards', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    tempClaudeRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-team-launch-'));
    tempTeamsBase = path.join(tempClaudeRoot, 'teams');
    fs.mkdirSync(tempTeamsBase, { recursive: true });
  });

  afterEach(() => {
    fs.rmSync(tempClaudeRoot, { recursive: true, force: true });
  });

  it('reuses the alive run instead of spawning a duplicate launch', async () => {
    const teamName = 'team-alpha';
    const teamDir = path.join(tempTeamsBase, teamName);
    fs.mkdirSync(teamDir, { recursive: true });
    fs.writeFileSync(
      path.join(teamDir, 'config.json'),
      JSON.stringify({
        name: teamName,
        projectPath: process.cwd(),
        members: [{ name: 'team-lead', agentType: 'team-lead' }, { name: 'dev' }],
      })
    );

    const svc = new TeamProvisioningService();
    const aliveRun = {
      runId: 'alive-run-1',
      teamName,
      request: { cwd: process.cwd() },
      child: Object.assign(new EventEmitter(), {
        stdin: { writable: true },
        stdout: new EventEmitter(),
        stderr: new EventEmitter(),
      }),
      processKilled: false,
      cancelRequested: false,
    };

    (svc as any).runs.set(aliveRun.runId, aliveRun);
    (svc as any).aliveRunByTeam.set(teamName, aliveRun.runId);

    const response = await svc.launchTeam({ teamName, cwd: process.cwd() }, () => {});

    expect(response.runId).toBe(aliveRun.runId);
  });

  it('does not reuse an alive run when cwd differs', async () => {
    const teamName = 'team-alpha';
    const currentCwd = fs.mkdtempSync(path.join(tempClaudeRoot, 'current-'));
    const nextCwd = fs.mkdtempSync(path.join(tempClaudeRoot, 'next-'));
    const teamDir = path.join(tempTeamsBase, teamName);
    fs.mkdirSync(teamDir, { recursive: true });
    fs.writeFileSync(
      path.join(teamDir, 'config.json'),
      JSON.stringify({
        name: teamName,
        projectPath: currentCwd,
        members: [{ name: 'team-lead', agentType: 'team-lead' }, { name: 'dev' }],
      })
    );

    const svc = new TeamProvisioningService();
    const aliveRun = {
      runId: 'alive-run-1',
      teamName,
      request: { cwd: currentCwd },
      child: Object.assign(new EventEmitter(), {
        stdin: { writable: true },
        stdout: new EventEmitter(),
        stderr: new EventEmitter(),
      }),
      processKilled: false,
      cancelRequested: false,
    };

    (svc as any).runs.set(aliveRun.runId, aliveRun);
    (svc as any).aliveRunByTeam.set(teamName, aliveRun.runId);

    await expect(svc.launchTeam({ teamName, cwd: nextCwd }, () => {})).rejects.toThrow(
      `Team "${teamName}" is already running in "${path.resolve(currentCwd)}".`
    );
  });

  it('fails closed when an alive run cwd cannot be determined', async () => {
    const teamName = 'team-alpha';
    const nextCwd = fs.mkdtempSync(path.join(tempClaudeRoot, 'next-'));
    const teamDir = path.join(tempTeamsBase, teamName);
    fs.mkdirSync(teamDir, { recursive: true });
    fs.writeFileSync(
      path.join(teamDir, 'config.json'),
      JSON.stringify({
        name: teamName,
        members: [{ name: 'team-lead', agentType: 'team-lead' }, { name: 'dev' }],
      })
    );

    const svc = new TeamProvisioningService();
    const aliveRun = {
      runId: 'alive-run-1',
      teamName,
      request: { cwd: '' },
      child: Object.assign(new EventEmitter(), {
        stdin: { writable: true },
        stdout: new EventEmitter(),
        stderr: new EventEmitter(),
      }),
      processKilled: false,
      cancelRequested: false,
      spawnContext: { cwd: '' },
    };

    (svc as any).runs.set(aliveRun.runId, aliveRun);
    (svc as any).aliveRunByTeam.set(teamName, aliveRun.runId);

    await expect(svc.launchTeam({ teamName, cwd: nextCwd }, () => {})).rejects.toThrow(
      `Team "${teamName}" is already running, but its cwd could not be determined.`
    );
  });
});
