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

import { User, PrimaryBadge } from "@library/shared-types";
import { prisma } from "../lib/prisma";
import { SuggestionStatus } from "@prisma/client";
import {
  validateUrl,
  validateSlug,
  validateStringLength,
  MAX_LENGTHS,
} from "../utils/validation";

export class UserService {
  private prisma = prisma;

  async getAllUsers(): Promise<User[]> {
    const users = await this.prisma.user.findMany({
      orderBy: { username: "asc" },
    });

    return users.map((user) => ({
      id: user.id,
      discordId: user.discordId,
      username: user.username,
      email: user.email,
      avatar: user.avatar || undefined,
      slug: user.slug || undefined,
      displayName: user.displayName || undefined,
      bio: user.bio || undefined,
      profilePublic: user.profilePublic,
      primaryBadge: (user.primaryBadge as PrimaryBadge) || undefined,
      website: user.website || undefined,
      discordServer: user.discordServer || undefined,
      bluesky: user.bluesky || undefined,
      github: user.github || undefined,
      linkedin: user.linkedin || undefined,
      twitch: user.twitch || undefined,
      youtube: user.youtube || undefined,
      isAdmin: user.isAdmin,
      isBanned: user.isBanned,
      inDiscord: user.inDiscord,
      isVip: user.isVip,
      isMod: user.isMod,
      isStaff: user.isStaff,
    }));
  }

  async getUserById(id: string): Promise<User | null> {
    const user = await this.prisma.user.findUnique({
      where: { id },
    });

    if (!user) {
      return null;
    }

    return {
      id: user.id,
      discordId: user.discordId,
      username: user.username,
      email: user.email,
      avatar: user.avatar || undefined,
      slug: user.slug || undefined,
      displayName: user.displayName || undefined,
      bio: user.bio || undefined,
      profilePublic: user.profilePublic,
      primaryBadge: (user.primaryBadge as PrimaryBadge) || undefined,
      website: user.website || undefined,
      discordServer: user.discordServer || undefined,
      bluesky: user.bluesky || undefined,
      github: user.github || undefined,
      linkedin: user.linkedin || undefined,
      twitch: user.twitch || undefined,
      youtube: user.youtube || undefined,
      isAdmin: user.isAdmin,
      isBanned: user.isBanned,
      inDiscord: user.inDiscord,
      isVip: user.isVip,
      isMod: user.isMod,
      isStaff: user.isStaff,
    };
  }

  async banUser(id: string): Promise<User | null> {
    const user = await this.prisma.user.update({
      where: { id },
      data: { isBanned: true },
    });

    return {
      id: user.id,
      discordId: user.discordId,
      username: user.username,
      email: user.email,
      avatar: user.avatar || undefined,
      slug: user.slug || undefined,
      displayName: user.displayName || undefined,
      bio: user.bio || undefined,
      profilePublic: user.profilePublic,
      primaryBadge: (user.primaryBadge as PrimaryBadge) || undefined,
      website: user.website || undefined,
      discordServer: user.discordServer || undefined,
      bluesky: user.bluesky || undefined,
      github: user.github || undefined,
      linkedin: user.linkedin || undefined,
      twitch: user.twitch || undefined,
      youtube: user.youtube || undefined,
      isAdmin: user.isAdmin,
      isBanned: user.isBanned,
      inDiscord: user.inDiscord,
      isVip: user.isVip,
      isMod: user.isMod,
      isStaff: user.isStaff,
    };
  }

  async unbanUser(id: string): Promise<User | null> {
    const user = await this.prisma.user.update({
      where: { id },
      data: { isBanned: false },
    });

    return {
      id: user.id,
      discordId: user.discordId,
      username: user.username,
      email: user.email,
      avatar: user.avatar || undefined,
      slug: user.slug || undefined,
      displayName: user.displayName || undefined,
      bio: user.bio || undefined,
      profilePublic: user.profilePublic,
      primaryBadge: (user.primaryBadge as PrimaryBadge) || undefined,
      website: user.website || undefined,
      discordServer: user.discordServer || undefined,
      bluesky: user.bluesky || undefined,
      github: user.github || undefined,
      linkedin: user.linkedin || undefined,
      twitch: user.twitch || undefined,
      youtube: user.youtube || undefined,
      isAdmin: user.isAdmin,
      isBanned: user.isBanned,
      inDiscord: user.inDiscord,
      isVip: user.isVip,
      isMod: user.isMod,
      isStaff: user.isStaff,
    };
  }

  async isUserBanned(id: string): Promise<boolean> {
    const user = await this.prisma.user.findUnique({
      where: { id },
      select: { isBanned: true },
    });

    return user?.isBanned ?? false;
  }

  async getUserBySlug(slug: string): Promise<User | null> {
    const user = await this.prisma.user.findFirst({
      where: { slug },
    });

    if (!user) {
      return null;
    }

    return {
      id: user.id,
      discordId: user.discordId,
      username: user.username,
      email: user.email,
      avatar: user.avatar || undefined,
      slug: user.slug || undefined,
      displayName: user.displayName || undefined,
      bio: user.bio || undefined,
      profilePublic: user.profilePublic,
      primaryBadge: (user.primaryBadge as PrimaryBadge) || undefined,
      website: user.website || undefined,
      discordServer: user.discordServer || undefined,
      bluesky: user.bluesky || undefined,
      github: user.github || undefined,
      linkedin: user.linkedin || undefined,
      twitch: user.twitch || undefined,
      youtube: user.youtube || undefined,
      isAdmin: user.isAdmin,
      isBanned: user.isBanned,
      inDiscord: user.inDiscord,
      isVip: user.isVip,
      isMod: user.isMod,
      isStaff: user.isStaff,
    };
  }

  async updateUserSettings(
    id: string,
    updates: {
      slug?: string;
      displayName?: string;
      bio?: string;
      profilePublic?: boolean;
      primaryBadge?: PrimaryBadge;
      website?: string;
      discordServer?: string;
      bluesky?: string;
      github?: string;
      linkedin?: string;
      twitch?: string;
      youtube?: string;
    }
  ): Promise<User | null> {
    // Validate slug format
    if (updates.slug && !validateSlug(updates.slug)) {
      throw new Error("Invalid slug format. Use only letters, numbers, hyphens, and underscores.");
    }

    // Validate string lengths
    if (!validateStringLength(updates.displayName, MAX_LENGTHS.DISPLAY_NAME)) {
      throw new Error(`Display name must be ${MAX_LENGTHS.DISPLAY_NAME} characters or less.`);
    }
    if (!validateStringLength(updates.bio, MAX_LENGTHS.BIO)) {
      throw new Error(`Bio must be ${MAX_LENGTHS.BIO} characters or less.`);
    }

    // Validate URLs
    const urlFields = [
      { field: "website", value: updates.website },
      { field: "discordServer", value: updates.discordServer },
      { field: "bluesky", value: updates.bluesky },
      { field: "github", value: updates.github },
      { field: "linkedin", value: updates.linkedin },
      { field: "twitch", value: updates.twitch },
      { field: "youtube", value: updates.youtube },
    ];

    for (const { field, value } of urlFields) {
      if (value && !validateUrl(value)) {
        throw new Error(`Invalid URL format for ${field}. Only http and https URLs are allowed.`);
      }
      if (!validateStringLength(value, MAX_LENGTHS.URL)) {
        throw new Error(`${field} URL must be ${MAX_LENGTHS.URL} characters or less.`);
      }
    }

    const user = await this.prisma.user.update({
      where: { id },
      data: updates,
    });

    return {
      id: user.id,
      discordId: user.discordId,
      username: user.username,
      email: user.email,
      avatar: user.avatar || undefined,
      slug: user.slug || undefined,
      displayName: user.displayName || undefined,
      bio: user.bio || undefined,
      profilePublic: user.profilePublic,
      primaryBadge: (user.primaryBadge as PrimaryBadge) || undefined,
      website: user.website || undefined,
      discordServer: user.discordServer || undefined,
      bluesky: user.bluesky || undefined,
      github: user.github || undefined,
      linkedin: user.linkedin || undefined,
      twitch: user.twitch || undefined,
      youtube: user.youtube || undefined,
      isAdmin: user.isAdmin,
      isBanned: user.isBanned,
      inDiscord: user.inDiscord,
      isVip: user.isVip,
      isMod: user.isMod,
      isStaff: user.isStaff,
    };
  }

  async getUserProfile(identifier: string): Promise<{
    id: string;
    username: string;
    displayName?: string | null;
    avatar?: string | null;
    bio?: string | null;
    slug?: string | null;
    primaryBadge?: PrimaryBadge | null;
    website?: string | null;
    discordServer?: string | null;
    bluesky?: string | null;
    github?: string | null;
    linkedin?: string | null;
    twitch?: string | null;
    youtube?: string | null;
    isStaff: boolean;
    isMod: boolean;
    isVip: boolean;
    inDiscord: boolean;
    profilePublic: boolean;
    createdAt: Date;
    achievementPoints: number;
    stats: {
      suggestionsCount: number;
      suggestionsAcceptedCount: number;
      likesCount: number;
      commentsCount: number;
    };
  } | null> {
    // Try to find by slug first, then by id if it's a valid ObjectId
    const isValidObjectId = /^[0-9a-f]{24}$/i.test(identifier);

    const whereConditions = isValidObjectId
      ? [{ slug: identifier }, { id: identifier }]
      : [{ slug: identifier }];

    const user = await this.prisma.user.findFirst({
      where: {
        OR: whereConditions,
      },
      include: {
        suggestions: {
          select: { id: true, status: true },
        },
        likes: {
          select: { id: true },
        },
        comments: {
          select: { id: true },
        },
      },
    });

    if (!user) {
      return null;
    }

    return {
      id: user.id,
      username: user.username,
      displayName: user.displayName,
      avatar: user.avatar,
      bio: user.bio,
      slug: user.slug,
      primaryBadge: user.primaryBadge as PrimaryBadge,
      website: user.website,
      discordServer: user.discordServer,
      bluesky: user.bluesky,
      github: user.github,
      linkedin: user.linkedin,
      twitch: user.twitch,
      youtube: user.youtube,
      isStaff: user.isStaff,
      isMod: user.isMod,
      isVip: user.isVip,
      inDiscord: user.inDiscord,
      profilePublic: user.profilePublic,
      createdAt: user.createdAt,
      achievementPoints: user.achievementPoints,
      stats: {
        suggestionsCount: user.suggestions.length,
        suggestionsAcceptedCount: user.suggestions.filter(
          (suggestion) => suggestion.status === SuggestionStatus.ACCEPTED
        ).length,
        likesCount: user.likes.length,
        commentsCount: user.comments.length,
      },
    };
  }
}
