import { prisma } from "../lib/prisma";
import type {
  Suggestion,
  SuggestionStatus,
  SuggestionEntity,
  CreateSuggestionDto,
  AcceptWithEditsDto,
} from "@library/shared-types";
import {
  GameStatus,
  BookStatus,
  MusicType,
  MusicStatus,
  ShowType,
  ShowStatus,
  MangaStatus,
} from "@library/shared-types";
import { GameService } from "./game.service";
import { BookService } from "./book.service";
import { MusicService } from "./music.service";
import { ArtService } from "./art.service";
import { ShowService } from "./show.service";
import { MangaService } from "./manga.service";

const gameService = new GameService();
const bookService = new BookService();
const musicService = new MusicService();
const artService = new ArtService();
const showService = new ShowService();
const mangaService = new MangaService();

interface SuggestionUser {
  id: string;
  username: string;
  avatar: string | null;
  inDiscord: boolean;
  isVip: boolean;
  isMod: boolean;
  isStaff: boolean;
}

function mapUser(user: {
  id: string;
  username: string;
  avatar: string | null;
  inDiscord: boolean;
  isVip: boolean;
  isMod: boolean;
  isStaff: boolean;
}): SuggestionUser {
  return {
    id: user.id,
    username: user.username,
    avatar: user.avatar,
    inDiscord: user.inDiscord,
    isVip: user.isVip,
    isMod: user.isMod,
    isStaff: user.isStaff,
  };
}

function mapSuggestion(suggestion: {
  id: string;
  userId: string;
  user: {
    id: string;
    username: string;
    avatar: string | null;
    inDiscord: boolean;
    isVip: boolean;
    isMod: boolean;
    isStaff: boolean;
  };
  entityType: string;
  status: string;
  declineReason: string | null;
  title: string;
  gameData: unknown;
  bookData: unknown;
  musicData: unknown;
  artData: unknown;
  showData: unknown;
  mangaData: unknown;
  createdAt: Date;
  updatedAt: Date;
}): Suggestion {
  return {
    id: suggestion.id,
    userId: suggestion.userId,
    user: mapUser(suggestion.user) as Suggestion["user"],
    entityType: suggestion.entityType as SuggestionEntity,
    status: suggestion.status as SuggestionStatus,
    declineReason: suggestion.declineReason ?? undefined,
    title: suggestion.title,
    gameData: suggestion.gameData as Suggestion["gameData"],
    bookData: suggestion.bookData as Suggestion["bookData"],
    musicData: suggestion.musicData as Suggestion["musicData"],
    artData: suggestion.artData as Suggestion["artData"],
    showData: suggestion.showData as Suggestion["showData"],
    mangaData: suggestion.mangaData as Suggestion["mangaData"],
    createdAt: suggestion.createdAt,
    updatedAt: suggestion.updatedAt,
  };
}

function getMusicType(type?: string): MusicType {
  switch (type) {
    case "ALBUM":
      return MusicType.album;
    case "SINGLE":
      return MusicType.single;
    case "EP":
      return MusicType.ep;
    default:
      return MusicType.album;
  }
}

function getShowType(type?: string): ShowType {
  switch (type) {
    case "TV_SERIES":
      return ShowType.tvSeries;
    case "ANIME":
      return ShowType.anime;
    case "FILM":
      return ShowType.film;
    case "DOCUMENTARY":
      return ShowType.documentary;
    default:
      return ShowType.tvSeries;
  }
}

export const SuggestionService = {
  async getAllSuggestions(filters?: {
    status?: SuggestionStatus;
    entityType?: SuggestionEntity;
  }): Promise<Suggestion[]> {
    const suggestions = await prisma.suggestion.findMany({
      where: {
        ...(filters?.status && { status: filters.status }),
        ...(filters?.entityType && { entityType: filters.entityType }),
      },
      include: {
        user: true,
      },
      orderBy: {
        createdAt: "desc",
      },
    });

    return suggestions.map(mapSuggestion);
  },

  async getUserSuggestions(userId: string): Promise<Suggestion[]> {
    const suggestions = await prisma.suggestion.findMany({
      where: {
        userId,
      },
      include: {
        user: true,
      },
      orderBy: {
        createdAt: "desc",
      },
    });

    return suggestions.map(mapSuggestion);
  },

  async getSuggestionById(id: string): Promise<Suggestion | null> {
    const suggestion = await prisma.suggestion.findUnique({
      where: { id },
      include: {
        user: true,
      },
    });

    if (!suggestion) {
      return null;
    }

    return mapSuggestion(suggestion);
  },

  async createSuggestion(
    userId: string,
    data: CreateSuggestionDto
  ): Promise<Suggestion> {
    const pendingCount = await prisma.suggestion.count({
      where: {
        userId,
        status: "UNREVIEWED",
      },
    });

    if (pendingCount >= 5) {
      throw new Error("You can only have 5 pending suggestions at a time");
    }

    const entityDataField = `${data.entityType.toLowerCase()}Data`;

    const suggestionData: Record<string, unknown> = {
      userId,
      entityType: data.entityType,
      title: data.title,
    };

    // Extract the data without entityType and title
    const { entityType: _et, title: _t, ...entityData } = data;
    suggestionData[entityDataField] = entityData;

    const suggestion = await prisma.suggestion.create({
      data: suggestionData as Parameters<typeof prisma.suggestion.create>[0]["data"],
      include: {
        user: true,
      },
    });

    return mapSuggestion(suggestion);
  },

  async acceptSuggestion(id: string): Promise<Suggestion> {
    const suggestion = await prisma.suggestion.findUnique({
      where: { id },
      include: { user: true },
    });

    if (!suggestion) {
      throw new Error("Suggestion not found");
    }

    if (suggestion.status !== "UNREVIEWED") {
      throw new Error("Suggestion has already been reviewed");
    }

    // Create the entity based on type with "Want to X" status
    switch (suggestion.entityType) {
      case "GAME": {
        const gameData = suggestion.gameData as {
          platform?: string;
          notes?: string;
          coverImage?: string;
        } | null;
        await gameService.createGame({
          title: suggestion.title,
          platform: gameData?.platform,
          status: GameStatus.backlog,
          notes: gameData?.notes,
          coverImage: gameData?.coverImage,
        });
        break;
      }
      case "BOOK": {
        const bookData = suggestion.bookData as {
          author: string;
          isbn?: string;
          notes?: string;
          coverImage?: string;
        } | null;
        await bookService.createBook({
          title: suggestion.title,
          author: bookData?.author ?? "Unknown",
          isbn: bookData?.isbn,
          status: BookStatus.toRead,
          notes: bookData?.notes,
          coverImage: bookData?.coverImage,
        });
        break;
      }
      case "MUSIC": {
        const musicData = suggestion.musicData as {
          artist: string;
          type: string;
          notes?: string;
          coverArt?: string;
        } | null;
        await musicService.createMusic({
          title: suggestion.title,
          artist: musicData?.artist ?? "Unknown",
          type: getMusicType(musicData?.type),
          status: MusicStatus.wantToListen,
          notes: musicData?.notes,
          coverArt: musicData?.coverArt,
        });
        break;
      }
      case "ART": {
        const artData = suggestion.artData as {
          artist: string;
          description?: string;
          imageUrl: string;
        } | null;
        await artService.createArt({
          title: suggestion.title,
          artist: artData?.artist ?? "Unknown",
          description: artData?.description,
          imageUrl: artData?.imageUrl ?? "",
        });
        break;
      }
      case "SHOW": {
        const showData = suggestion.showData as {
          type: string;
          notes?: string;
          coverImage?: string;
        } | null;
        await showService.createShow({
          title: suggestion.title,
          type: getShowType(showData?.type),
          status: ShowStatus.wantToWatch,
          notes: showData?.notes,
          coverImage: showData?.coverImage,
        });
        break;
      }
      case "MANGA": {
        const mangaData = suggestion.mangaData as {
          author: string;
          notes?: string;
          coverImage?: string;
        } | null;
        await mangaService.createManga({
          title: suggestion.title,
          author: mangaData?.author ?? "Unknown",
          status: MangaStatus.wantToRead,
          notes: mangaData?.notes,
          coverImage: mangaData?.coverImage,
        });
        break;
      }
    }

    // Update suggestion status
    const updatedSuggestion = await prisma.suggestion.update({
      where: { id },
      data: { status: "ACCEPTED" },
      include: { user: true },
    });

    return mapSuggestion(updatedSuggestion);
  },

  async acceptSuggestionWithEdits(id: string, editedData: AcceptWithEditsDto): Promise<Suggestion> {
    const suggestion = await prisma.suggestion.findUnique({
      where: { id },
      include: { user: true },
    });

    if (!suggestion) {
      throw new Error("Suggestion not found");
    }

    if (suggestion.status !== "UNREVIEWED") {
      throw new Error("Suggestion has already been reviewed");
    }

    // Create the entity based on type with edited data
    switch (suggestion.entityType) {
      case "GAME": {
        await gameService.createGame({
          title: editedData.title || suggestion.title,
          platform: editedData.platform,
          status: GameStatus.backlog,
          notes: editedData.notes,
          coverImage: editedData.coverImage,
        });
        break;
      }
      case "BOOK": {
        await bookService.createBook({
          title: editedData.title || suggestion.title,
          author: editedData.author || "Unknown",
          isbn: editedData.isbn,
          status: BookStatus.toRead,
          notes: editedData.notes,
          coverImage: editedData.coverImage,
        });
        break;
      }
      case "MUSIC": {
        await musicService.createMusic({
          title: editedData.title || suggestion.title,
          artist: editedData.artist || "Unknown",
          type: getMusicType(editedData.type),
          status: MusicStatus.wantToListen,
          notes: editedData.notes,
          coverArt: editedData.coverArt || editedData.coverImage,
        });
        break;
      }
      case "ART": {
        await artService.createArt({
          title: editedData.title || suggestion.title,
          artist: editedData.artist || "Unknown",
          description: editedData.description,
          imageUrl: editedData.imageUrl || "",
        });
        break;
      }
      case "SHOW": {
        await showService.createShow({
          title: editedData.title || suggestion.title,
          type: getShowType(editedData.type),
          status: ShowStatus.wantToWatch,
          notes: editedData.notes,
          coverImage: editedData.coverImage,
        });
        break;
      }
      case "MANGA": {
        await mangaService.createManga({
          title: editedData.title || suggestion.title,
          author: editedData.author || "Unknown",
          status: MangaStatus.wantToRead,
          notes: editedData.notes,
          coverImage: editedData.coverImage,
        });
        break;
      }
    }

    // Update suggestion status
    const updatedSuggestion = await prisma.suggestion.update({
      where: { id },
      data: { status: "ACCEPTED" },
      include: { user: true },
    });

    return mapSuggestion(updatedSuggestion);
  },

  async declineSuggestion(id: string, reason?: string): Promise<Suggestion> {
    const suggestion = await prisma.suggestion.findUnique({
      where: { id },
    });

    if (!suggestion) {
      throw new Error("Suggestion not found");
    }

    if (suggestion.status !== "UNREVIEWED") {
      throw new Error("Suggestion has already been reviewed");
    }

    const updatedSuggestion = await prisma.suggestion.update({
      where: { id },
      data: {
        status: "DECLINED",
        declineReason: reason,
      },
      include: { user: true },
    });

    return mapSuggestion(updatedSuggestion);
  },

  async deleteSuggestion(id: string, userId: string, isAdmin: boolean): Promise<Suggestion> {
    const suggestion = await prisma.suggestion.findUnique({
      where: { id },
      include: { user: true },
    });

    if (!suggestion) {
      throw new Error("Suggestion not found");
    }

    if (!isAdmin && suggestion.userId !== userId) {
      throw new Error("You can only delete your own suggestions");
    }

    if (suggestion.status !== "UNREVIEWED") {
      throw new Error("Cannot delete a suggestion that has already been reviewed");
    }

    await prisma.suggestion.delete({
      where: { id },
    });

    return mapSuggestion(suggestion);
  },
};
