import cors from '@fastify/cors';
import Fastify from 'fastify';
import { registerJsonBodyParser } from './json-body.js';
import { registerBoardRoutes } from './routes/board.js';
import { registerCrudRoutes } from './routes/crud.js';
import { registerDdlRoutes } from './routes/ddl.js';
import { registerErrorLogRoutes } from './routes/error-log.js';
import { registerHealthRoutes } from './routes/health.js';
import { registerAppConfigRoutes } from './routes/app-config.js';
import { registerBaseballTicketBayRoutes } from './routes/baseball-ticket-bay.js';
import { registerChatRoutes } from './routes/chat.js';
import { registerNotificationRoutes } from './routes/notification.js';
import { registerPlanRoutes } from './routes/plan.js';
import { registerPhotoPrismRoutes } from './routes/photoprism.js';
import { registerReaderRoutes } from './routes/reader.js';
import { registerResourceManagerRoutes } from './routes/resource-manager.js';
import { registerPlayAppRoutes } from './routes/play-app.js';
import { registerRuntimeRoutes } from './routes/runtime.js';
import { registerServerCommandRoutes } from './routes/server-command.js';
import { registerSchemaRoutes } from './routes/schema.js';
import { registerSharedResourceTokenRoutes } from './routes/shared-resource-token.js';
import { registerStockAlertRoutes } from './routes/stock-alert.js';
import { registerTestAppRoutes } from './routes/test-app.js';
import { registerTextMemoRoutes } from './routes/text-memo.js';
import { registerVisitorHistoryRoutes } from './routes/visitor-history.js';
import { shouldPersistNotFoundErrorLog } from './not-found.js';
import { createErrorLog } from './services/error-log-service.js';
import {
  isRuntimeDraining,
  trackHttpRequestFinished,
  trackHttpRequestStarted,
} from './services/runtime-drain-service.js';

function isDrainAllowedPath(method: string, url: string) {
  return method === 'OPTIONS'
    || url === '/'
    || url === '/api'
    || url === '/health'
    || url.startsWith('/api/runtime')
    || url.startsWith('/api/server-commands');
}

export function createApp() {
  const app = Fastify({
    logger: true,
    routerOptions: {
      maxParamLength: 20000,
    },
  });

  app.register(cors, {
    origin: true,
  });

  app.addHook('onRequest', async (request, reply) => {
    trackHttpRequestStarted();
    let finished = false;
    const finalize = () => {
      if (finished) {
        return;
      }

      finished = true;
      trackHttpRequestFinished();
      reply.raw.off('finish', finalize);
      reply.raw.off('close', finalize);
    };

    reply.raw.on('finish', finalize);
    reply.raw.on('close', finalize);

    if (isRuntimeDraining() && !isDrainAllowedPath(request.method, request.url)) {
      reply.code(503).send({
        ok: false,
        message: '이 서버는 배포 전환 중이라 새 요청을 받지 않습니다. 잠시 후 다시 시도해 주세요.',
      });
      return reply;
    }
  });

  registerJsonBodyParser(app);
  app.register(registerBoardRoutes);
  app.register(registerHealthRoutes);
  app.register(registerAppConfigRoutes);
  app.register(registerBaseballTicketBayRoutes);
  app.register(registerChatRoutes);
  app.register(registerSchemaRoutes);
  app.register(registerDdlRoutes);
  app.register(registerCrudRoutes);
  app.register(registerStockAlertRoutes);
  app.register(registerTestAppRoutes);
  app.register(registerErrorLogRoutes);
  app.register(registerNotificationRoutes);
  app.register(registerPlanRoutes);
  app.register(registerPhotoPrismRoutes);
  app.register(registerPlayAppRoutes);
  app.register(registerReaderRoutes);
  app.register(registerResourceManagerRoutes);
  app.register(registerRuntimeRoutes);
  app.register(registerSharedResourceTokenRoutes);
  app.register(registerServerCommandRoutes);
  app.register(registerTextMemoRoutes);
  app.register(registerVisitorHistoryRoutes);

  app.setNotFoundHandler(async (request, reply) => {
    if (shouldPersistNotFoundErrorLog(request.url)) {
      try {
        await createErrorLog({
          source: 'server',
          sourceLabel: '워크서버 API',
          errorType: 'NotFound',
          errorMessage: `Route not found: ${request.method} ${request.url}`,
          statusCode: 404,
          requestMethod: request.method,
          requestPath: request.url,
          context: {
            route: request.routeOptions.url,
          },
        });
      } catch (loggingError) {
        app.log.error(loggingError, 'Failed to persist 404 error log');
      }
    }

    reply.status(404);
    return {
      message: '요청한 경로를 찾을 수 없습니다.',
    };
  });

  app.setErrorHandler(async (error, request, reply) => {
    const handledError = error instanceof Error ? error : new Error(String(error));
    const errorWithMeta = handledError as Error & { statusCode?: number; code?: string };
    const statusCode = typeof errorWithMeta.statusCode === 'number'
      ? Number(errorWithMeta.statusCode)
      : 500;

    try {
      await createErrorLog({
        source: 'server',
        sourceLabel: '워크서버 API',
        errorType: errorWithMeta.code ?? handledError.name ?? 'ServerError',
        errorName: handledError.name,
        errorMessage: handledError.message || '서버 오류가 발생했습니다.',
        stackTrace: handledError.stack,
        statusCode,
        requestMethod: request.method,
        requestPath: request.url,
        context: {
          route: request.routeOptions.url,
          params: (request.params as Record<string, unknown> | undefined) ?? null,
          query: (request.query as Record<string, unknown> | undefined) ?? null,
        },
      });
    } catch (loggingError) {
      app.log.error(loggingError, 'Failed to persist server error log');
    }

    app.log.error(handledError);
    reply.status(statusCode >= 400 ? statusCode : 500);
    return {
      message: handledError.message || '요청 처리에 실패했습니다.',
    };
  });

  return app;
}
