import type { FastifyInstance } from 'fastify';
import { z } from 'zod';
import { hasErrorLogViewAccessToken } from '../services/error-log-service.js';
import {
  ensureVisitorHistoryTables,
  getVisitorClientByClientId,
  listVisitorClients,
  listVisitorClientsQuerySchema,
  listVisitorHistories,
  trackVisit,
  trackVisitSchema,
  updateVisitorNickname,
  updateVisitorNicknameSchema,
  visitorHistoryQuerySchema,
} from '../services/visitor-history-service.js';

export async function registerVisitorHistoryRoutes(app: FastifyInstance) {
  function requireHistoryAccessToken(
    request: { headers: Record<string, unknown> },
    reply: Parameters<FastifyInstance['get']>[1] extends (request: any, reply: infer T) => any ? T : any,
  ) {
    if (hasErrorLogViewAccessToken(request.headers['x-access-token'] as string | string[] | undefined)) {
      return true;
    }

    void reply.code(403).send({
      message: '권한 토큰이 등록된 사용자만 방문 이력을 조회할 수 있습니다.',
    });
    return false;
  }

  app.post('/api/history/setup', async () => {
    await ensureVisitorHistoryTables();

    return {
      ok: true,
    };
  });

  app.post('/api/history/track', async (request) => {
    const bodyPayload =
      request.body && typeof request.body === 'object'
        ? (request.body as Record<string, unknown>)
        : {};
    const clientIdFromHeader = String(request.headers['x-client-id'] ?? '').trim();
    const payload = trackVisitSchema.parse({
      clientId: clientIdFromHeader || bodyPayload.clientId,
      url: bodyPayload.url,
      eventType: bodyPayload.eventType,
      userAgent:
        typeof bodyPayload.userAgent === 'string'
          ? bodyPayload.userAgent
          : String(request.headers['user-agent'] ?? ''),
    });

    const client = await trackVisit(payload, request.ip);

    return {
      ok: true,
      client,
    };
  });

  app.get('/api/history/visitors', async (request, reply) => {
    if (!requireHistoryAccessToken(request, reply)) {
      return;
    }

    const query = listVisitorClientsQuerySchema.parse(request.query ?? {});
    const items = await listVisitorClients(query.limit ?? 100, {
      search: query.search,
      clientId: query.clientId,
      nickname: query.nickname,
      path: query.path,
      visitedFrom: query.visitedFrom,
      visitedTo: query.visitedTo,
    });

    return {
      ok: true,
      items,
    };
  });

  app.get('/api/history/visitors/:clientId', async (request, reply) => {
    if (!requireHistoryAccessToken(request, reply)) {
      return;
    }

    const clientId = z.string().trim().min(1).parse((request.params as { clientId: string }).clientId);
    const query = visitorHistoryQuerySchema.parse(request.query ?? {});
    const client = await getVisitorClientByClientId(clientId);

    if (!client) {
      return reply.code(404).send({
        message: '방문자를 찾을 수 없습니다.',
      });
    }

    const visits = await listVisitorHistories(clientId, query.limit ?? 200);

    return {
      ok: true,
      client,
      visits,
    };
  });

  app.patch('/api/history/visitors/:clientId/nickname', async (request, reply) => {
    if (!requireHistoryAccessToken(request, reply)) {
      return;
    }

    const clientId = z.string().trim().min(1).parse((request.params as { clientId: string }).clientId);
    const payload = updateVisitorNicknameSchema.parse(request.body ?? {});
    const client = await updateVisitorNickname(clientId, payload.nickname);

    if (!client) {
      return reply.code(404).send({
        message: '닉네임을 수정할 방문자를 찾을 수 없습니다.',
      });
    }

    return {
      ok: true,
      client,
    };
  });
}
