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

import { FastifyPluginAsync } from "fastify";
import { User, AuditAction, AuditCategory, PrimaryBadge } from "@library/shared-types";
import { UserService } from "../../services/user.service";
import { AuditService } from "../../services/audit.service";
import { adminGuard } from "../../middleware/admin-guard";

interface UpdateUserSettingsBody {
  slug?: string;
  displayName?: string;
  bio?: string;
  profilePublic?: boolean;
  primaryBadge?: PrimaryBadge;
  website?: string;
  discordServer?: string;
  bluesky?: string;
  github?: string;
  linkedin?: string;
  twitch?: string;
  youtube?: string;
}

interface UserProfileResponse {
  id: string;
  username: string;
  displayName?: string;
  avatar?: string;
  bio?: string;
  slug?: string;
  primaryBadge?: PrimaryBadge;
  website?: string;
  discordServer?: string;
  bluesky?: string;
  github?: string;
  linkedin?: string;
  twitch?: string;
  youtube?: string;
  badges: {
    isStaff: boolean;
    isMod: boolean;
    isVip: boolean;
    inDiscord: boolean;
  };
  stats: {
    suggestionsCount: number;
    suggestionsAcceptedCount: number;
    likesCount: number;
    commentsCount: number;
  };
  createdAt: Date;
}

const usersRoutes: FastifyPluginAsync = async (app) => {
  const userService = new UserService();

  app.get<{ Reply: User[] }>(
    "/",
    {
      preValidation: [app.authenticate, adminGuard],
    },
    async () => {
      return userService.getAllUsers();
    }
  );

  app.get<{ Reply: User }>(
    "/me",
    {
      preValidation: [app.authenticate],
    },
    async (request) => {
      const currentUser = request.user as { id: string };
      const user = await userService.getUserById(currentUser.id);
      if (!user) {
        throw new Error("User not found");
      }
      return user;
    }
  );

  app.put<{ Body: UpdateUserSettingsBody; Reply: User | { error: string } }>(
    "/me",
    {
      preValidation: [app.authenticate],
      preHandler: [app.csrfProtection],
    },
    async (request, reply) => {
      const currentUser = request.user as { id: string };
      const updates = request.body;

      // If slug is being updated, check if it's unique
      if (updates.slug) {
        const existingUser = await userService.getUserBySlug(updates.slug);
        if (existingUser && existingUser.id !== currentUser.id) {
          return reply.code(400).send({ error: "Slug already taken" });
        }
      }

      const updatedUser = await userService.updateUserSettings(
        currentUser.id,
        updates
      );

      if (!updatedUser) {
        return reply.code(404).send({ error: "User not found" });
      }

      return updatedUser;
    }
  );

  app.get<{
    Params: { identifier: string };
    Reply: UserProfileResponse | { error: string };
  }>(
    "/profile/:identifier",
    async (request, reply) => {
      const { identifier } = request.params;

      try {
        const profile = await userService.getUserProfile(identifier);

        if (!profile) {
          return reply.code(404).send({ error: "User not found" });
        }

      if (!profile.profilePublic) {
        // Check if the requesting user is viewing their own profile
        const currentUser = request.user as { id: string } | undefined;
        if (!currentUser || currentUser.id !== profile.id) {
          return reply
            .code(403)
            .send({ error: "This profile is private" });
        }
      }

      return {
        id: profile.id,
        username: profile.username,
        displayName: profile.displayName,
        avatar: profile.avatar,
        bio: profile.bio,
        slug: profile.slug,
        primaryBadge: profile.primaryBadge,
        website: profile.website,
        discordServer: profile.discordServer,
        bluesky: profile.bluesky,
        github: profile.github,
        linkedin: profile.linkedin,
        twitch: profile.twitch,
        youtube: profile.youtube,
        badges: {
          isStaff: profile.isStaff,
          isMod: profile.isMod,
          isVip: profile.isVip,
          inDiscord: profile.inDiscord,
        },
        stats: profile.stats,
        createdAt: profile.createdAt,
      };
      } catch (error) {
        app.log.error({ err: error }, "Error fetching profile");
        return reply.code(500).send({ error: "Failed to fetch profile" });
      }
    }
  );

  app.get<{ Params: { id: string }; Reply: User | null }>(
    "/:id",
    {
      preValidation: [app.authenticate, adminGuard],
    },
    async (request) => {
      const { id } = request.params;
      return userService.getUserById(id);
    }
  );

  app.post<{ Params: { id: string }; Reply: User | { error: string } }>(
    "/:id/ban",
    {
      preValidation: [app.authenticate, adminGuard],
      preHandler: [app.csrfProtection],
    },
    async (request, reply) => {
      const { id } = request.params;
      const currentUser = request.user as { id: string };

      if (currentUser.id === id) {
        return reply.code(400).send({ error: "You cannot ban yourself" });
      }

      const user = await userService.banUser(id);
      if (!user) {
        return reply.code(404).send({ error: "User not found" });
      }

      await AuditService.logFromRequest(request, {
        action: AuditAction.userBan,
        category: AuditCategory.admin,
        targetUserId: id,
        details: `Banned user: ${user.username}`,
      });

      return user;
    }
  );

  app.post<{ Params: { id: string }; Reply: User | { error: string } }>(
    "/:id/unban",
    {
      preValidation: [app.authenticate, adminGuard],
      preHandler: [app.csrfProtection],
    },
    async (request, reply) => {
      const { id } = request.params;
      const user = await userService.unbanUser(id);
      if (!user) {
        return reply.code(404).send({ error: "User not found" });
      }

      await AuditService.logFromRequest(request, {
        action: AuditAction.userUnban,
        category: AuditCategory.admin,
        targetUserId: id,
        details: `Unbanned user: ${user.username}`,
      });

      return user;
    }
  );

  app.post<{ Params: { id: string }; Reply: User | { error: string } }>(
    "/:id/make-private",
    {
      preValidation: [app.authenticate, adminGuard],
      preHandler: [app.csrfProtection],
    },
    async (request, reply) => {
      const { id } = request.params;
      const user = await userService.updateUserSettings(id, { profilePublic: false });
      if (!user) {
        return reply.code(404).send({ error: "User not found" });
      }

      await AuditService.logFromRequest(request, {
        action: AuditAction.entryUpdate,
        category: AuditCategory.admin,
        targetUserId: id,
        details: `Admin made profile private for user: ${user.username}`,
      });

      return user;
    }
  );

  app.put<{ Params: { id: string }; Body: UpdateUserSettingsBody; Reply: User | { error: string } }>(
    "/:id",
    {
      preValidation: [app.authenticate, adminGuard],
      preHandler: [app.csrfProtection],
    },
    async (request, reply) => {
      const { id } = request.params;
      const updates = request.body;

      // If slug is being updated, check if it's unique
      if (updates.slug) {
        const existingUser = await userService.getUserBySlug(updates.slug);
        if (existingUser && existingUser.id !== id) {
          return reply.code(400).send({ error: "Slug already taken" });
        }
      }

      const updatedUser = await userService.updateUserSettings(id, updates);
      if (!updatedUser) {
        return reply.code(404).send({ error: "User not found" });
      }

      await AuditService.logFromRequest(request, {
        action: AuditAction.entryUpdate,
        category: AuditCategory.admin,
        targetUserId: id,
        details: `Admin updated profile for user: ${updatedUser.username}`,
      });

      return updatedUser;
    }
  );
};

export default usersRoutes;
