/**
 * /ui — serves the @mcpctl/web SPA bundle.
 *
 * In production the bundle lives at /usr/share/mcpd/web (installed by the
 * RPM in Stage 6); in dev it lives at <repo>/src/web/dist after a
 * `pnpm --filter @mcpctl/web build`. The location is overridable via the
 * `MCPD_WEB_ROOT` env var so deployers can move it freely.
 *
 * If the directory is missing we log a warning and skip — mcpd still serves
 * the API. That lets the dev tree run without forcing a web build first.
 *
 * SPA routing: anything under /ui/<path> that's not a file falls back to
 * index.html so client-side react-router routes work on direct hits.
 */
import path from 'node:path';
import { existsSync, statSync, readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import type { FastifyInstance } from 'fastify';
import fastifyStatic from '@fastify/static';

const DEFAULT_PROD_ROOT = '/usr/share/mcpd/web';

function resolveWebRoot(): string | null {
  const fromEnv = process.env['MCPD_WEB_ROOT'];
  if (fromEnv !== undefined && fromEnv !== '') {
    return existsSync(fromEnv) ? fromEnv : null;
  }
  if (existsSync(DEFAULT_PROD_ROOT)) return DEFAULT_PROD_ROOT;

  // Dev fallback: walk up from this file to find <repo>/src/web/dist.
  // After bun compile this path doesn't resolve, which is fine — prod uses
  // DEFAULT_PROD_ROOT or MCPD_WEB_ROOT instead.
  try {
    const here = path.dirname(fileURLToPath(import.meta.url));
    const candidate = path.resolve(here, '../../../web/dist');
    if (existsSync(candidate)) return candidate;
  } catch {
    // import.meta.url unavailable in some bundled envs — skip.
  }
  return null;
}

export async function registerWebUi(app: FastifyInstance): Promise<void> {
  const root = resolveWebRoot();
  if (root === null) {
    app.log.warn(
      `web UI bundle not found (set MCPD_WEB_ROOT, or place a build at ${DEFAULT_PROD_ROOT}); /ui will return 404`,
    );
    return;
  }
  if (!statSync(root).isDirectory()) {
    app.log.warn({ root }, 'web UI root is not a directory; /ui will return 404');
    return;
  }

  await app.register(fastifyStatic, {
    root,
    prefix: '/ui/',
    wildcard: false,
  });

  // Read index.html once at startup; the SPA fallback below serves it
  // verbatim for every unmatched /ui/* path so client-side routing works
  // on direct hits. Reading once also dodges a per-request `sendFile`
  // call — there's only one file ever served from this handler.
  const indexHtmlPath = path.join(root, 'index.html');
  const indexHtml = existsSync(indexHtmlPath)
    ? readFileSync(indexHtmlPath, 'utf-8')
    : null;
  if (indexHtml === null) {
    app.log.warn({ root }, 'web UI index.html missing; deep links to /ui/<path> will 404');
  }

  app.get('/ui/*', (_request, reply) => {
    if (indexHtml === null) {
      reply.code(404);
      return { error: 'index.html missing from web UI bundle' };
    }
    reply.type('text/html').send(indexHtml);
    return reply;
  });
  // Cover the bare /ui (no trailing slash) too.
  app.get('/ui', (_request, reply) => {
    return reply.redirect('/ui/');
  });

  app.log.info({ root }, 'web UI mounted at /ui');
}
