import type { FastifyInstance } from 'fastify';
import { z } from 'zod';
import { hasErrorLogViewAccessToken } from '../services/error-log-service.js';
import {
  BoardPostAutomationLockedError,
  boardPostPayloadSchema,
  createBoardPost,
  deleteBoardPost,
  ensureBoardPostsTable,
  getBoardPost,
  listBoardPosts,
  receiveBoardPostAutomation,
  updateBoardPost,
} from '../services/board-service.js';

export async function registerBoardRoutes(app: FastifyInstance) {
  function isLoopbackAddress(value: string | null | undefined) {
    const normalizedValue = String(value ?? '').trim();
    return normalizedValue === '127.0.0.1' || normalizedValue === '::1' || normalizedValue === '::ffff:127.0.0.1';
  }

  function hasBoardAutomationAccess(request: { headers: Record<string, unknown>; ip?: string; socket?: { remoteAddress?: string | null }; raw?: { socket?: { remoteAddress?: string | null } } }) {
    if (hasErrorLogViewAccessToken(request.headers['x-access-token'] as string | string[] | undefined)) {
      return true;
    }

    return isLoopbackAddress(request.ip) || isLoopbackAddress(request.raw?.socket?.remoteAddress) || isLoopbackAddress(request.socket?.remoteAddress);
  }

  function requireBoardAutomationAccess(
    request: { headers: Record<string, unknown>; ip?: string; socket?: { remoteAddress?: string | null }; raw?: { socket?: { remoteAddress?: string | null } } },
    reply: Parameters<FastifyInstance['get']>[1] extends (request: any, reply: infer T) => any ? T : any,
  ) {
    if (hasBoardAutomationAccess(request)) {
      return true;
    }

    void reply.code(403).send({
      message: '권한 토큰이 등록된 사용자만 자동화 접수를 할 수 있습니다.',
    });
    return false;
  }

  async function respondWithBoardSetup() {
    await ensureBoardPostsTable();

    return {
      ok: true,
      table: 'board_posts',
    };
  }

  async function respondWithBoardPosts() {
    const items = await listBoardPosts();

    return {
      ok: true,
      items,
    };
  }

  app.get('/api/board/setup', async () => respondWithBoardSetup());
  app.post('/api/board/setup', async () => {
    return respondWithBoardSetup();
  });

  app.get('/api/board/posts', async () => respondWithBoardPosts());
  app.get('/api/board/items', async () => respondWithBoardPosts());

  app.get('/api/board/posts/:id', async (request, reply) => {
    const id = z.coerce.number().int().positive().parse((request.params as { id: string }).id);
    const item = await getBoardPost(id);

    if (!item) {
      return reply.code(404).send({
        message: '게시글을 찾을 수 없습니다.',
      });
    }

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

  app.post('/api/board/posts', async (request) => {
    const item = await createBoardPost(boardPostPayloadSchema.parse(request.body ?? {}));

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

  app.post('/api/board/posts/:id/actions/automation-receive', async (request, reply) => {
    if (!requireBoardAutomationAccess(request, reply)) {
      return;
    }

    const id = z.coerce.number().int().positive().parse((request.params as { id: string }).id);
    const result = await receiveBoardPostAutomation(id);

    if (!result) {
      return reply.code(404).send({
        message: '자동화 접수할 게시글을 찾을 수 없습니다.',
      });
    }

    return {
      ok: true,
      item: result.item,
      planItemIds: result.planItemIds,
      alreadyReceived: result.alreadyReceived,
    };
  });

  app.patch('/api/board/posts/:id', async (request, reply) => {
    const id = z.coerce.number().int().positive().parse((request.params as { id: string }).id);
    let item;

    try {
      item = await updateBoardPost(id, boardPostPayloadSchema.parse(request.body ?? {}));
    } catch (error) {
      if (error instanceof BoardPostAutomationLockedError) {
        return reply.code(409).send({
          message: error.message,
        });
      }

      throw error;
    }

    if (!item) {
      return reply.code(404).send({
        message: '수정할 게시글을 찾을 수 없습니다.',
      });
    }

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

  app.delete('/api/board/posts/:id', async (request, reply) => {
    const id = z.coerce.number().int().positive().parse((request.params as { id: string }).id);
    let deleted;

    try {
      deleted = await deleteBoardPost(id);
    } catch (error) {
      if (error instanceof BoardPostAutomationLockedError) {
        return reply.code(409).send({
          message: error.message,
        });
      }

      throw error;
    }

    if (!deleted) {
      return reply.code(404).send({
        message: '삭제할 게시글을 찾을 수 없습니다.',
      });
    }

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