import test from 'node:test';
import assert from 'node:assert/strict';
import os from 'os';
import path from 'path';
import { rm } from 'fs/promises';

import { startServer } from './index.js';
import { saveState } from './services/SaveState.js';
import { shiftTimer } from './services/ShiftTimer.js';
import { incidentScheduler } from './services/IncidentScheduler.js';
import { vmManager } from './services/VMManager.js';

async function bootServer(testId) {
  process.env.CONTENT_DIR = path.resolve(process.cwd(), '../content');
  process.env.SAVE_DIR = path.join(os.tmpdir(), `sc-server-route-test-${testId}-${Date.now()}`);

  await saveState._writeQueue.catch(() => {});
  saveState._savePath = null;
  saveState._state = null;
  saveState._writeQueue = Promise.resolve();
  await rm(process.env.SAVE_DIR, { recursive: true, force: true });

  const originalEnsureWorkstationLive = vmManager.ensureWorkstationLive.bind(vmManager);
  const originalGetState = vmManager.getState.bind(vmManager);

  vmManager.ensureWorkstationLive = async () => ({ ok: true, started: false });
  vmManager.getState = async (vmId) => (vmId === 'workstation' ? 'running' : 'shut off');

  const { server, wss } = await startServer({ port: 0, host: '127.0.0.1' });
  const address = server.address();
  const port = typeof address === 'object' && address ? address.port : 0;
  const baseUrl = `http://127.0.0.1:${port}`;

  async function sessionToken() {
    const response = await fetch(`${baseUrl}/api/session`);
    const payload = await response.json();
    return payload.token;
  }

  async function authedFetch(pathname, init = {}) {
    const token = await sessionToken();
    const headers = new Headers(init.headers ?? {});
    headers.set('Authorization', `Bearer ${token}`);
    if (init.body && !headers.has('Content-Type')) {
      headers.set('Content-Type', 'application/json');
    }

    return fetch(`${baseUrl}${pathname}`, {
      ...init,
      headers
    });
  }

  async function close() {
    shiftTimer.stop();
    incidentScheduler.stop();
    await Promise.all([
      new Promise((resolve, reject) => {
        server.close((error) => {
          if (error) reject(error);
          else resolve();
        });
      }),
      new Promise((resolve) => wss.close(resolve))
    ]);

    vmManager.ensureWorkstationLive = originalEnsureWorkstationLive;
    vmManager.getState = originalGetState;
  }

  return { baseUrl, authedFetch, sessionToken, close };
}

test('session endpoint issues a token and protected state route requires auth', async () => {
  const ctx = await bootServer('session-auth');

  try {
    const sessionResponse = await fetch(`${ctx.baseUrl}/api/session`);
    assert.equal(sessionResponse.status, 200);
    const sessionPayload = await sessionResponse.json();
    assert.ok(typeof sessionPayload.token === 'string');

    const unauthResponse = await fetch(`${ctx.baseUrl}/api/state`);
    assert.equal(unauthResponse.status, 401);

    const authResponse = await ctx.authedFetch('/api/state');
    assert.equal(authResponse.status, 200);
    const state = await authResponse.json();
    assert.equal(state.trust, 50);
    assert.ok(state.shift);
    assert.ok(Array.isArray(state.certifications));
    assert.ok(Array.isArray(state.shiftHistory));
  } finally {
    await ctx.close();
  }
});

test('docs route enforces id validation and unlock gating', async () => {
  const ctx = await bootServer('docs');

  try {
    const invalidResponse = await ctx.authedFetch('/api/docs/bad$id');
    assert.equal(invalidResponse.status, 400);

    const unlockedResponse = await ctx.authedFetch('/api/docs/onboarding');
    assert.equal(unlockedResponse.status, 200);
    const unlockedDoc = await unlockedResponse.json();
    assert.equal(unlockedDoc.id, 'onboarding');

    const lockedResponse = await ctx.authedFetch('/api/docs/server-admin-guide');
    assert.equal(lockedResponse.status, 403);
  } finally {
    await ctx.close();
  }
});

test('sage route validates input and returns authored quest help', async () => {
  const ctx = await bootServer('sage');

  try {
    const badResponse = await ctx.authedFetch('/api/sage/message', {
      method: 'POST',
      body: JSON.stringify({ message: '' })
    });
    assert.equal(badResponse.status, 400);

    const goodResponse = await ctx.authedFetch('/api/sage/message', {
      method: 'POST',
      body: JSON.stringify({ message: 'give me a hint' })
    });
    assert.equal(goodResponse.status, 200);
    const payload = await goodResponse.json();
    assert.match(payload.response, /\.ssh/i);
    assert.ok(Array.isArray(payload.followUps));
  } finally {
    await ctx.close();
  }
});

test('vms route returns current VM states under auth', async () => {
  const ctx = await bootServer('vms');

  try {
    const response = await ctx.authedFetch('/api/vms');
    assert.equal(response.status, 200);
    const vms = await response.json();
    assert.equal(vms.length, 3);
    assert.equal(vms.find((vm) => vm.id === 'workstation')?.state, 'running');
  } finally {
    await ctx.close();
  }
});

test('ticket and mail routes validate request payloads', async () => {
  const ctx = await bootServer('payload-validation');

  try {
    const ticketResponse = await ctx.authedFetch('/api/tickets/T001/complete', {
      method: 'POST',
      body: JSON.stringify({ branchId: 123 })
    });
    assert.equal(ticketResponse.status, 400);

    const mailResponse = await ctx.authedFetch('/api/mail/mail-T001-initial/reply', {
      method: 'POST',
      body: JSON.stringify({ choice: '0' })
    });
    assert.equal(mailResponse.status, 400);
  } finally {
    await ctx.close();
  }
});
