import { resolve } from 'path';
import { existsSync } from 'fs';
import { readFile } from 'fs/promises';
import Fastify, { FastifyError, FastifyRequest, FastifyReply } from 'fastify';
import cors from '@fastify/cors';
import fastifyStatic from '@fastify/static';
import { createModuleLogger } from './utils/logger.js';
import { registerRoutes, type AppModules } from './wiring.js';

const log = createModuleLogger('server');

export async function buildServer(modules?: AppModules) {
  const app = Fastify({ logger: true });

  // CORS - allow configured origins or all in production
  const isProduction = process.env.NODE_ENV === 'production';
  await app.register(cors, {
    origin: isProduction ? true : ['http://localhost:3000', 'http://localhost:5173'],
    methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  });

  // Localhost access restriction (development only)
  if (!isProduction) {
    app.addHook('onRequest', async (request, reply) => {
      const host = request.hostname?.split(':')[0];
      if (host !== 'localhost' && host !== '127.0.0.1') {
        log.warn({ host, ip: request.ip }, 'Blocked non-localhost access');
        reply.code(403).send({ error: 'Access restricted to localhost' });
      }
    });
  }

  // Structured error handler
  app.setErrorHandler((error: FastifyError, request: FastifyRequest, reply: FastifyReply) => {
    log.error({ err: error, url: request.url, method: request.method }, 'Request error');

    const statusCode = error.statusCode ?? 500;
    reply.code(statusCode).send({
      error: statusCode >= 500 ? 'Internal Server Error' : error.message,
      statusCode,
    });
  });

  // Static file serving for data directory
  await app.register(fastifyStatic, {
    root: resolve(process.cwd(), 'data'),
    prefix: '/static/',
    decorateReply: false,
  });

  // Serve frontend SPA build from web/dist (if it exists)
  const webDistDir = resolve(process.cwd(), 'web', 'dist');
  if (existsSync(webDistDir)) {
    await app.register(fastifyStatic, {
      root: webDistDir,
      prefix: '/',
      decorateReply: false,
      wildcard: false,
    });

    // SPA fallback: for any unmatched GET that isn't /api or /static, serve index.html
    app.setNotFoundHandler(async (request, reply) => {
      if (request.method === 'GET' && !request.url.startsWith('/api') && !request.url.startsWith('/static') && !request.url.startsWith('/health')) {
        const indexPath = resolve(webDistDir, 'index.html');
        if (existsSync(indexPath)) {
          const html = await readFile(indexPath, 'utf-8');
          reply.type('text/html').send(html);
          return;
        }
      }
      reply.code(404).send({ error: 'Not Found', statusCode: 404 });
    });
  }

  // Health check (available before route plugins)
  app.get('/health', async () => ({ status: 'ok', timestamp: new Date().toISOString() }));

  // Register all route plugins if modules are provided
  if (modules) {
    await registerRoutes(app, modules);
  }

  return app;
}
