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

import type {
  Activity,
  ActivityFeedResponse,
  ActivityUser,
  SuggestionActivity,
  LikeActivity,
  CommentActivity,
  AchievementActivity,
} from "@library/shared-types";
import { ACHIEVEMENTS, ActivityType } from "@library/shared-types";
import { prisma } from "../lib/prisma";

export class ActivityService {
  private prisma = prisma;

  constructor() {}

  /**
   * Get activity feed with pagination.
   */
  async getActivityFeed(
    limit = 50,
    offset = 0,
    userId?: string
  ): Promise<ActivityFeedResponse> {
    // Fetch suggestions, likes, comments, and achievements
    const [suggestions, likes, comments, achievements] = await Promise.all([
      this.getSuggestionActivities(limit, offset, userId),
      this.getLikeActivities(limit, offset, userId),
      this.getCommentActivities(limit, offset, userId),
      this.getAchievementActivities(limit, offset, userId),
    ]);

    // Combine and sort by createdAt
    const activities: Activity[] = [
      ...suggestions,
      ...likes,
      ...comments,
      ...achievements,
    ].sort((a, b) => {
      return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
    });

    // Apply pagination to combined results
    const paginatedActivities = activities.slice(offset, offset + limit);
    const hasMore = activities.length > offset + limit;

    return {
      activities: paginatedActivities,
      total: activities.length,
      hasMore,
    };
  }

  /**
   * Get suggestion activities.
   */
  private async getSuggestionActivities(
    limit: number,
    offset: number,
    userId?: string
  ): Promise<SuggestionActivity[]> {
    const where = userId
      ? { userId, user: { profilePublic: true, isBanned: false } }
      : { user: { profilePublic: true, isBanned: false } };

    const suggestions = await this.prisma.suggestion.findMany({
      where,
      include: {
        user: {
          select: {
            id: true,
            username: true,
            slug: true,
            avatar: true,
            primaryBadge: true,
            isVip: true,
            isMod: true,
            isStaff: true,
          },
        },
      },
      orderBy: { createdAt: "desc" },
      take: limit * 2, // Get more since we're combining
    });

    return suggestions.map((suggestion) => ({
      id: `suggestion-${suggestion.id}`,
      type: ActivityType.suggestion,
      user: suggestion.user as ActivityUser,
      entityType: suggestion.entityType,
      suggestionTitle: suggestion.title,
      status: suggestion.status,
      createdAt: suggestion.createdAt,
    }));
  }

  /**
   * Get like activities.
   */
  private async getLikeActivities(
    limit: number,
    offset: number,
    userId?: string
  ): Promise<LikeActivity[]> {
    const where = userId
      ? { userId, user: { profilePublic: true, isBanned: false } }
      : { user: { profilePublic: true, isBanned: false } };

    const likes = await this.prisma.like.findMany({
      where,
      include: {
        user: {
          select: {
            id: true,
            username: true,
            slug: true,
            avatar: true,
            primaryBadge: true,
            isVip: true,
            isMod: true,
            isStaff: true,
          },
        },
      },
      orderBy: { createdAt: "desc" },
      take: limit * 2,
    });

    // For each like, fetch the entity title
    const likesWithTitles = await Promise.all(
      likes.map(async (like): Promise<LikeActivity> => {
        const entityTitle = await this.getEntityTitle(
          like.entityType,
          like.entityId
        );
        return {
          id: `like-${like.id}`,
          type: ActivityType.like,
          user: like.user as ActivityUser,
          entityType: like.entityType,
          entityId: like.entityId,
          entityTitle,
          createdAt: like.createdAt,
        };
      })
    );

    return likesWithTitles;
  }

  /**
   * Get comment activities.
   */
  private async getCommentActivities(
    limit: number,
    offset: number,
    userId?: string
  ): Promise<CommentActivity[]> {
    const where = userId
      ? { userId, user: { profilePublic: true, isBanned: false } }
      : { user: { profilePublic: true, isBanned: false } };

    const comments = await this.prisma.comment.findMany({
      where,
      include: {
        user: {
          select: {
            id: true,
            username: true,
            slug: true,
            avatar: true,
            primaryBadge: true,
            isVip: true,
            isMod: true,
            isStaff: true,
          },
        },
        game: { select: { id: true, title: true } },
        book: { select: { id: true, title: true } },
        music: { select: { id: true, title: true } },
        art: { select: { id: true, title: true } },
        show: { select: { id: true, title: true } },
        manga: { select: { id: true, title: true } },
      },
      orderBy: { createdAt: "desc" },
      take: limit * 2,
    });

    return comments.map((comment) => {
      let entityType = "";
      let entityId = "";
      let entityTitle = "";

      if (comment.game) {
        entityType = "game";
        entityId = comment.game.id;
        entityTitle = comment.game.title;
      } else if (comment.book) {
        entityType = "book";
        entityId = comment.book.id;
        entityTitle = comment.book.title;
      } else if (comment.music) {
        entityType = "music";
        entityId = comment.music.id;
        entityTitle = comment.music.title;
      } else if (comment.art) {
        entityType = "art";
        entityId = comment.art.id;
        entityTitle = comment.art.title;
      } else if (comment.show) {
        entityType = "show";
        entityId = comment.show.id;
        entityTitle = comment.show.title;
      } else if (comment.manga) {
        entityType = "manga";
        entityId = comment.manga.id;
        entityTitle = comment.manga.title;
      }

      // Get first 100 characters of comment
      const commentPreview =
        comment.content.length > 100
          ? `${comment.content.slice(0, 100)}...`
          : comment.content;

      return {
        id: `comment-${comment.id}`,
        type: ActivityType.comment,
        user: comment.user as ActivityUser,
        entityType,
        entityId,
        entityTitle,
        commentPreview,
        createdAt: comment.createdAt,
      };
    });
  }

  /**
   * Get achievement activities.
   */
  private async getAchievementActivities(
    limit: number,
    offset: number,
    userId?: string
  ): Promise<AchievementActivity[]> {
    const where = userId
      ? {
          userId,
          earned: true,
          user: { profilePublic: true, isBanned: false },
        }
      : { earned: true, user: { profilePublic: true, isBanned: false } };

    const userAchievements = await this.prisma.userAchievement.findMany({
      where,
      include: {
        user: {
          select: {
            id: true,
            username: true,
            slug: true,
            avatar: true,
            primaryBadge: true,
            isVip: true,
            isMod: true,
            isStaff: true,
          },
        },
      },
      orderBy: { earnedAt: "desc" },
      take: limit * 2,
    });

    return userAchievements
      .filter((ua) => ua.earnedAt) // Only show earned achievements
      .map((ua) => {
        const achievement = ACHIEVEMENTS[ua.achievementKey];
        return {
          id: `achievement-${ua.id}`,
          type: ActivityType.achievement,
          user: ua.user as ActivityUser,
          achievementKey: ua.achievementKey,
          achievementName: achievement.title,
          achievementIcon: achievement.icon,
          achievementPoints: achievement.points,
          createdAt: ua.earnedAt!,
        };
      });
  }

  /**
   * Helper to get entity title by type and ID.
   */
  private async getEntityTitle(
    entityType: string,
    entityId: string
  ): Promise<string> {
    switch (entityType) {
      case "game": {
        const game = await this.prisma.game.findUnique({
          where: { id: entityId },
          select: { title: true },
        });
        return game?.title || "Unknown Game";
      }
      case "book": {
        const book = await this.prisma.book.findUnique({
          where: { id: entityId },
          select: { title: true },
        });
        return book?.title || "Unknown Book";
      }
      case "music": {
        const music = await this.prisma.music.findUnique({
          where: { id: entityId },
          select: { title: true },
        });
        return music?.title || "Unknown Music";
      }
      case "art": {
        const art = await this.prisma.art.findUnique({
          where: { id: entityId },
          select: { title: true },
        });
        return art?.title || "Unknown Art";
      }
      case "show": {
        const show = await this.prisma.show.findUnique({
          where: { id: entityId },
          select: { title: true },
        });
        return show?.title || "Unknown Show";
      }
      case "manga": {
        const manga = await this.prisma.manga.findUnique({
          where: { id: entityId },
          select: { title: true },
        });
        return manga?.title || "Unknown Manga";
      }
      default:
        return "Unknown Item";
    }
  }
}
