/**
 * @copyright 2026 NHCarrigan
 * @license Naomi's Public License
 * @author Naomi Carrigan
 */

import { FastifyPluginAsync } from "fastify";
import { Game, CreateGameDto, UpdateGameDto, Comment, CreateCommentDto, AuditAction, AuditCategory, AchievementCategory } from "@library/shared-types";
import { GameService } from "../../services/game.service";
import { CommentService } from "../../services/comment.service";
import { AuditService } from "../../services/audit.service";
import { AchievementService } from "../../services/achievement.service";
import { adminGuard } from "../../middleware/admin-guard";
import { bannedGuard } from "../../middleware/banned-guard";

const gamesRoutes: FastifyPluginAsync = async (app) => {
  const gameService = new GameService();
  const commentService = new CommentService();

  // Get all games (public route)
  app.get<{ Reply: Game[] }>("/", async () => {
    return gameService.getAllGames();
  });

  // Get all games in a series (public route)
  app.get<{ Params: { seriesName: string }; Reply: Game[] }>(
    "/series/:seriesName",
    async (request) => {
      const { seriesName } = request.params;
      return gameService.getGamesBySeries(seriesName);
    }
  );

  // Get single game (public route)
  app.get<{ Params: { id: string }; Reply: Game | null }>(
    "/:id",
    async (request) => {
      const { id } = request.params;
      return gameService.getGameById(id);
    }
  );

  // Create game (protected admin route)
  app.post<{ Body: CreateGameDto; Reply: Game | { error: string } }>(
    "/",
    {
      preValidation: [app.authenticate, adminGuard],
      preHandler: [app.csrfProtection],
    },
    async (request, reply) => {
      try {
        const game = await gameService.createGame(request.body);
        await AuditService.logFromRequest(request, {
          action: AuditAction.entryCreate,
          category: AuditCategory.content,
          resourceType: "game",
          resourceId: game.id,
          details: `Created game: ${game.title}`,
        });
        return game;
      } catch (error) {
        if (error instanceof Error) {
          return reply.code(400).send({ error: error.message });
        }
        throw error;
      }
    }
  );

  // Update game (protected admin route)
  app.put<{
    Params: { id: string };
    Body: UpdateGameDto;
    Reply: Game | null | { error: string };
  }>(
    "/:id",
    {
      preValidation: [app.authenticate, adminGuard],
      preHandler: [app.csrfProtection],
    },
    async (request, reply) => {
      try {
        const { id } = request.params;
        const game = await gameService.updateGame(id, request.body);
        if (game) {
          await AuditService.logFromRequest(request, {
            action: AuditAction.entryUpdate,
            category: AuditCategory.content,
            resourceType: "game",
            resourceId: id,
            details: `Updated game: ${game.title}`,
          });
        }
        return game;
      } catch (error) {
        if (error instanceof Error) {
          return reply.code(400).send({ error: error.message });
        }
        throw error;
      }
    }
  );

  // Delete game (protected admin route)
  app.delete<{ Params: { id: string }; Reply: { success: boolean } }>(
    "/:id",
    {
      preValidation: [app.authenticate, adminGuard],
      preHandler: [app.csrfProtection],
    },
    async (request) => {
      const { id } = request.params;
      await gameService.deleteGame(id);
      await AuditService.logFromRequest(request, {
        action: AuditAction.entryDelete,
        category: AuditCategory.content,
        resourceType: "game",
        resourceId: id,
        details: `Deleted game with ID: ${id}`,
      });
      return { success: true };
    }
  );

  // Get comments for a game (public route)
  app.get<{ Params: { id: string }; Reply: Comment[] }>(
    "/:id/comments",
    async (request) => {
      const { id } = request.params;
      return commentService.getCommentsForGame(id);
    }
  );

  // Add comment to a game (authenticated users)
  app.post<{ Params: { id: string }; Body: CreateCommentDto; Reply: Comment }>(
    "/:id/comments",
    {
      preValidation: [app.authenticate, bannedGuard],
      preHandler: [app.csrfProtection],
    },
    async (request) => {
      const { id } = request.params;
      const userId = request.user.id;
      const comment = await commentService.createCommentForGame(id, userId, request.body);
      await AuditService.logFromRequest(request, {
        action: AuditAction.commentCreate,
        category: AuditCategory.content,
        resourceType: "game",
        resourceId: id,
        details: `Added comment to game`,
      });

      // Check for comment achievements
      const achievementService = new AchievementService();
      await achievementService.checkAchievements(
        userId,
        AchievementCategory.Comment,
        request
      );

      return comment;
    }
  );

  // Update comment (owner or admin)
  app.put<{ Params: { id: string; commentId: string }; Body: CreateCommentDto; Reply: Comment | { error: string } }>(
    "/:id/comments/:commentId",
    {
      preValidation: [app.authenticate],
      preHandler: [app.csrfProtection],
    },
    async (request, reply) => {
      const { id, commentId } = request.params;
      const userId = request.user.id;
      const isAdmin = request.user.isAdmin;

      const verification = await commentService.verifyCommentOwnership(commentId, "game", id);

      if (!verification.exists) {
        return reply.code(404).send({ error: "Comment not found" });
      }

      if (verification.comment?.userId !== userId && !isAdmin) {
        return reply.code(403).send({ error: "You can only edit your own comments" });
      }

      const comment = await commentService.updateComment(commentId, request.body.content);
      await AuditService.logFromRequest(request, {
        action: AuditAction.commentUpdate,
        category: AuditCategory.content,
        resourceType: "game",
        resourceId: id,
        details: `Updated comment ${commentId} on game`,
      });
      return comment;
    }
  );

  // Delete comment (owner or admin)
  app.delete<{ Params: { id: string; commentId: string }; Reply: { success: boolean } | { error: string } }>(
    "/:id/comments/:commentId",
    {
      preValidation: [app.authenticate],
      preHandler: [app.csrfProtection],
    },
    async (request, reply) => {
      const { id, commentId } = request.params;
      const userId = request.user.id;
      const isAdmin = request.user.isAdmin;

      const verification = await commentService.verifyCommentOwnership(commentId, "game", id);

      if (!verification.exists) {
        return reply.code(404).send({ error: "Comment not found" });
      }

      if (verification.comment?.userId !== userId && !isAdmin) {
        return reply.code(403).send({ error: "You can only delete your own comments" });
      }

      await commentService.deleteComment(commentId);
      await AuditService.logFromRequest(request, {
        action: AuditAction.commentDelete,
        category: isAdmin && verification.comment?.userId !== userId ? AuditCategory.admin : AuditCategory.content,
        resourceType: "game",
        resourceId: id,
        details: `Deleted comment ${commentId} from game`,
      });
      return { success: true };
    }
  );
};

export default gamesRoutes;