import type { FastifyInstance, FastifyRequest } from 'fastify';
import { BadRequestError, ForbiddenError, NotFoundError } from '@bytelyst/errors';
import { PRODUCT_ID } from '../../lib/product-config.js';
import { getUserId } from '../../lib/request-context.js';
import { getIpcBridge } from '../../lib/ipc-bridge.js';
import { InstallPluginSchema, PluginIdParamsSchema, ReloadResponseSchema } from './types.js';

function buildAuth(req: FastifyRequest): Record<string, unknown> {
  const userId = getUserId(req);
  const role = (req.jwtPayload as Record<string, unknown> | undefined)?.role ?? 'user';
  return { userId, role, productId: PRODUCT_ID, isPlatformAuth: !!req.jwtPayload };
}

export async function pluginRoutes(app: FastifyInstance) {
  const bridge = getIpcBridge();

  app.get('/api/plugins', async req => {
    if (!bridge.isRunning) {
      return { plugins: [] };
    }

    const resp = await bridge.call('list_plugins', { auth: buildAuth(req) });
    if (resp.error) throw new BadRequestError(resp.error.message);
    return resp.result;
  });

  app.get('/api/plugins/:id', async req => {
    const parsed = PluginIdParamsSchema.safeParse(req.params);
    if (!parsed.success)
      throw new BadRequestError(parsed.error.issues.map(issue => issue.message).join('; '));
    if (!bridge.isRunning) throw new NotFoundError('Plugin not found');

    const resp = await bridge.call('get_plugin', {
      auth: buildAuth(req),
      pluginId: parsed.data.id,
    });
    if (resp.error) {
      if (resp.error.code === -32001) throw new NotFoundError('Plugin not found');
      throw new BadRequestError(resp.error.message);
    }
    return resp.result;
  });

  app.post('/api/plugins/install', async req => {
    const parsed = InstallPluginSchema.safeParse(req.body);
    if (!parsed.success)
      throw new BadRequestError(parsed.error.issues.map(issue => issue.message).join('; '));
    if (!bridge.isRunning) throw new BadRequestError('IPC bridge unavailable');

    const resp = await bridge.call('install_plugin', {
      auth: buildAuth(req),
      pluginId: parsed.data.pluginId,
    });
    if (resp.error) throw new BadRequestError(resp.error.message);
    return resp.result;
  });

  app.delete('/api/plugins/:id', async req => {
    const parsed = PluginIdParamsSchema.safeParse(req.params);
    if (!parsed.success)
      throw new BadRequestError(parsed.error.issues.map(issue => issue.message).join('; '));
    if (!bridge.isRunning) throw new NotFoundError('Plugin not found');

    const resp = await bridge.call('uninstall_plugin', {
      auth: buildAuth(req),
      pluginId: parsed.data.id,
    });
    if (resp.error) {
      if (resp.error.code === -32001) throw new NotFoundError('Plugin not found');
      throw new BadRequestError(resp.error.message);
    }
    return resp.result;
  });

  // POST /api/plugins/reload — hot-reload the orchestrator's plugin registry
  // without a restart. Admin role OR a platform-signed JWT is required; the
  // orchestrator enforces the same check, but we reject here too so a
  // forbidden caller gets the canonical `ForbiddenError` envelope instead of
  // a raw IPC error passthrough. In-flight sessions that have already
  // resolved a plugin handle are unaffected (see `PluginLoader::reload`).
  app.post('/api/plugins/reload', async req => {
    if (!bridge.isRunning) throw new BadRequestError('IPC bridge unavailable');

    const auth = buildAuth(req);
    if (auth.role !== 'admin' && !auth.isPlatformAuth) {
      throw new ForbiddenError('plugin reload requires admin role or platform auth');
    }

    const resp = await bridge.call('reload_plugins', { auth });
    if (resp.error) {
      if (resp.error.code === -32003) {
        throw new ForbiddenError(resp.error.message);
      }
      throw new BadRequestError(resp.error.message);
    }

    const parsed = ReloadResponseSchema.safeParse(resp.result);
    if (!parsed.success) {
      req.log.error({ issues: parsed.error.issues }, 'reload_plugins returned malformed payload');
      throw new BadRequestError('orchestrator returned malformed reload stats');
    }

    req.log.info({ stats: parsed.data }, 'plugins reloaded');
    return parsed.data;
  });
}
