import { randomUUID } from 'node:crypto';
import { createRouter } from '@@/create-router';
import * as errors from '@@/custom-errors';
import { getUserIntegration, upsertUserIntegration, deleteUserIntegration } from 'officerdb';
import { deriveRelayToken, registerUserToken, unregisterUserToken } from './relay-auth';
import { getRelayPort, getUserRelayStatus, getUserTargets } from './relay';
import { captureScreenshot, evaluateJS, navigateTo } from './cdp';

export const browserRouter = createRouter();

async function getOpts(userId: number) {
  const port = getRelayPort();
  const integration = await getUserIntegration(userId, 'browser-relay');
  const salt = (integration?.config as { tokenSalt?: string } | null)?.tokenSalt ?? randomUUID();
  if (!integration) {
    await upsertUserIntegration({ userId, provider: 'browser-relay', config: { tokenSalt: salt } });
  }
  const token = registerUserToken(userId, port, salt);
  return { relayPort: port, userToken: token };
}

// GET /extension-download used to zip the extension directory on the fly, shelling out to `zip`. It had
// no caller: BrowserRelay.tsx links straight at /browser-relay-extension.zip, a real file in public/.

browserRouter.get('/status', async (ctx) => {
  const user = ctx.get('user');
  const status = getUserRelayStatus(user.id);
  return ctx.json(status);
});

browserRouter.get('/relay-token', async (ctx) => {
  const user = ctx.get('user');
  const port = getRelayPort();
  let integration = await getUserIntegration(user.id, 'browser-relay');
  if (!integration) {
    const salt = randomUUID();
    integration = await upsertUserIntegration({
      userId: user.id,
      provider: 'browser-relay',
      config: { tokenSalt: salt },
    });
  }
  const salt = (integration.config as { tokenSalt: string }).tokenSalt;
  const token = registerUserToken(user.id, port, salt);
  return ctx.json({ token, port });
});

browserRouter.post('/relay-token/regenerate', async (ctx) => {
  const user = ctx.get('user');
  const port = getRelayPort();
  const oldIntegration = await getUserIntegration(user.id, 'browser-relay');
  if (oldIntegration) {
    const oldSalt = (oldIntegration.config as { tokenSalt?: string })?.tokenSalt;
    if (oldSalt) {
      const oldToken = deriveRelayToken(user.id, port, oldSalt);
      unregisterUserToken(oldToken);
    }
  }
  const newSalt = randomUUID();
  await upsertUserIntegration({ userId: user.id, provider: 'browser-relay', config: { tokenSalt: newSalt } });
  const token = registerUserToken(user.id, port, newSalt);
  return ctx.json({ token, port });
});

browserRouter.delete('/relay-token', async (ctx) => {
  const user = ctx.get('user');
  const port = getRelayPort();
  const integration = await getUserIntegration(user.id, 'browser-relay');
  if (integration) {
    const salt = (integration.config as { tokenSalt?: string })?.tokenSalt;
    if (salt) {
      const oldToken = deriveRelayToken(user.id, port, salt);
      unregisterUserToken(oldToken);
    }
    await deleteUserIntegration(user.id, 'browser-relay');
  }
  return ctx.json({ ok: true });
});

browserRouter.get('/targets', async (ctx) => {
  const user = ctx.get('user');
  const targets = getUserTargets(user.id);
  return ctx.json(
    targets.map((t) => ({
      id: t.targetId,
      sessionId: t.sessionId,
      type: t.targetInfo.type ?? 'page',
      title: t.targetInfo.title ?? '',
      url: t.targetInfo.url ?? '',
    })),
  );
});

browserRouter.get('/targets/:id/screenshot', async (ctx) => {
  const user = ctx.get('user');
  const targetId = ctx.req.param('id');
  const format = (ctx.req.query('format') as 'png' | 'jpeg') || 'png';

  const opts = await getOpts(user.id);
  // Find session ID for the target
  const targets = getUserTargets(user.id);
  const target = targets.find((t) => t.targetId === targetId);
  if (!target) throw errors.NOT_FOUND('Target not found');

  try {
    const data = await captureScreenshot({ ...opts, targetId: target.sessionId }, format);
    return ctx.json({ data, format });
  } catch (err) {
    throw errors.INTERNAL_SERVER_ERROR(err instanceof Error ? err.message : 'Screenshot failed');
  }
});

browserRouter.post('/targets/:id/evaluate', async (ctx) => {
  const user = ctx.get('user');
  const targetId = ctx.req.param('id');
  const body = ctx.get('body') as { expression?: string };

  if (!body.expression) throw errors.BAD_REQUEST('expression is required');

  const opts = await getOpts(user.id);
  const targets = getUserTargets(user.id);
  const target = targets.find((t) => t.targetId === targetId);
  if (!target) throw errors.NOT_FOUND('Target not found');

  try {
    const result = await evaluateJS({ ...opts, targetId: target.sessionId }, body.expression);
    return ctx.json({ result });
  } catch (err) {
    throw errors.INTERNAL_SERVER_ERROR(err instanceof Error ? err.message : 'Evaluation failed');
  }
});

browserRouter.post('/targets/:id/navigate', async (ctx) => {
  const user = ctx.get('user');
  const targetId = ctx.req.param('id');
  const body = ctx.get('body') as { url?: string };

  if (!body.url) throw errors.BAD_REQUEST('url is required');

  const opts = await getOpts(user.id);
  const targets = getUserTargets(user.id);
  const target = targets.find((t) => t.targetId === targetId);
  if (!target) throw errors.NOT_FOUND('Target not found');

  try {
    await navigateTo({ ...opts, targetId: target.sessionId }, body.url);
    return ctx.json({ ok: true });
  } catch (err) {
    throw errors.INTERNAL_SERVER_ERROR(err instanceof Error ? err.message : 'Navigation failed');
  }
});

browserRouter.post('/targets/:id/activate', async (ctx) => {
  const user = ctx.get('user');
  const targetId = ctx.req.param('id');

  const { relayPort, userToken } = await getOpts(user.id);

  try {
    const res = await fetch(`http://127.0.0.1:${relayPort}/json/activate/${encodeURIComponent(targetId)}`, {
      headers: { 'x-officer-relay-token': userToken },
    });
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return ctx.json({ ok: true });
  } catch (err) {
    throw errors.INTERNAL_SERVER_ERROR(err instanceof Error ? err.message : 'Activate failed');
  }
});

browserRouter.post('/targets/:id/close', async (ctx) => {
  const user = ctx.get('user');
  const targetId = ctx.req.param('id');

  const { relayPort, userToken } = await getOpts(user.id);

  try {
    const res = await fetch(`http://127.0.0.1:${relayPort}/json/close/${encodeURIComponent(targetId)}`, {
      headers: { 'x-officer-relay-token': userToken },
    });
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return ctx.json({ ok: true });
  } catch (err) {
    throw errors.INTERNAL_SERVER_ERROR(err instanceof Error ? err.message : 'Close failed');
  }
});
