import { mkdirSync, readFileSync, rmSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { randomUUID } from 'node:crypto';
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { nowISO, appendEvent, emitEvent } from '../src/event-emitter.js';
import type { MACPEvent } from '../src/types.js';

function makeTmpDir(): string {
  const dir = join(tmpdir(), `macp-event-${randomUUID()}`);
  mkdirSync(dir, { recursive: true });
  return dir;
}

describe('nowISO', () => {
  it('returns a valid ISO timestamp', () => {
    const ts = nowISO();
    expect(() => new Date(ts)).not.toThrow();
    expect(new Date(ts).toISOString()).toBe(ts);
  });
});

describe('appendEvent', () => {
  let tmp: string;

  beforeEach(() => {
    tmp = makeTmpDir();
  });

  afterEach(() => {
    rmSync(tmp, { recursive: true, force: true });
  });

  it('appends event as ndjson line', () => {
    const eventsPath = join(tmp, 'events.ndjson');
    const event: MACPEvent = {
      event_id: 'evt-1',
      event_type: 'task.started',
      task_id: 'task-1',
      status: 'running',
      timestamp: nowISO(),
      source: 'test',
      message: 'Test event',
      metadata: {},
    };
    appendEvent(eventsPath, event);

    const content = readFileSync(eventsPath, 'utf-8');
    const lines = content.trim().split('\n');
    expect(lines).toHaveLength(1);
    const parsed = JSON.parse(lines[0]!);
    expect(parsed.event_id).toBe('evt-1');
    expect(parsed.event_type).toBe('task.started');
    expect(parsed.task_id).toBe('task-1');
  });

  it('appends multiple events', () => {
    const eventsPath = join(tmp, 'events.ndjson');
    const base: MACPEvent = {
      event_id: '',
      event_type: 'task.started',
      task_id: 'task-1',
      status: 'running',
      timestamp: nowISO(),
      source: 'test',
      message: '',
      metadata: {},
    };
    appendEvent(eventsPath, { ...base, event_id: 'evt-1', message: 'first' });
    appendEvent(eventsPath, { ...base, event_id: 'evt-2', message: 'second' });

    const lines = readFileSync(eventsPath, 'utf-8').trim().split('\n');
    expect(lines).toHaveLength(2);
  });

  it('creates parent directories', () => {
    const eventsPath = join(tmp, 'nested', 'deep', 'events.ndjson');
    const event: MACPEvent = {
      event_id: 'evt-1',
      event_type: 'task.started',
      task_id: 'task-1',
      status: 'running',
      timestamp: nowISO(),
      source: 'test',
      message: 'nested',
      metadata: {},
    };
    appendEvent(eventsPath, event);
    expect(readFileSync(eventsPath, 'utf-8')).toContain('nested');
  });
});

describe('emitEvent', () => {
  let tmp: string;

  beforeEach(() => {
    tmp = makeTmpDir();
  });

  afterEach(() => {
    rmSync(tmp, { recursive: true, force: true });
  });

  it('creates event with all required fields', () => {
    const eventsPath = join(tmp, 'events.ndjson');
    emitEvent(eventsPath, 'task.completed', 'task-42', 'completed', 'controller', 'Task done');

    const content = readFileSync(eventsPath, 'utf-8');
    const event = JSON.parse(content.trim());
    expect(event.event_id).toBeTruthy();
    expect(event.event_type).toBe('task.completed');
    expect(event.task_id).toBe('task-42');
    expect(event.status).toBe('completed');
    expect(event.source).toBe('controller');
    expect(event.message).toBe('Task done');
    expect(event.timestamp).toBeTruthy();
    expect(event.metadata).toEqual({});
  });

  it('includes metadata when provided', () => {
    const eventsPath = join(tmp, 'events.ndjson');
    emitEvent(eventsPath, 'task.failed', 'task-1', 'failed', 'worker', 'err', {
      exit_code: 1,
    });

    const event = JSON.parse(readFileSync(eventsPath, 'utf-8').trim());
    expect(event.metadata).toEqual({ exit_code: 1 });
  });

  it('generates unique event_ids', () => {
    const eventsPath = join(tmp, 'events.ndjson');
    emitEvent(eventsPath, 'task.started', 'task-1', 'running', 'test', 'a');
    emitEvent(eventsPath, 'task.started', 'task-1', 'running', 'test', 'b');

    const events = readFileSync(eventsPath, 'utf-8')
      .trim()
      .split('\n')
      .map((l) => JSON.parse(l));
    expect(events[0].event_id).not.toBe(events[1].event_id);
  });
});
