import prisma from '../config/database';
import { AIService } from './ai.service';

export class AIRecommendationsService {
  /**
   * Semantic Vector & Attribute-Based Similar Books Recommendation
   */
  static async getSimilarBooks(bookId: string, limit = 6) {
    const targetBook = await prisma.book.findUnique({
      where: { id: bookId },
      include: {
        bookAuthors: { include: { author: true } },
        bookCategories: { include: { category: true } },
      },
    });

    if (!targetBook) {
      throw new Error('Target book not found');
    }

    const authorIds = targetBook.bookAuthors.map((a) => a.authorId);
    const categoryIds = targetBook.bookCategories.map((c) => c.categoryId);

    // 1. Fetch candidate books in related categories or by related authors
    const candidates = await prisma.book.findMany({
      where: {
        id: { not: bookId },
        isActive: true,
      },
      include: {
        bookAuthors: { include: { author: true } },
        bookCategories: { include: { category: true } },
        images: { where: { isPrimary: true }, take: 1 },
        variants: { where: { isActive: true } },
      },
      take: 50,
      orderBy: { ratingAverage: 'desc' },
    });

    if (candidates.length === 0) return [];

    // 2. Generate target embedding
    const targetText = `${targetBook.title} ${targetBook.description || ''} ${targetBook.bookCategories.map((c) => c.category.name).join(' ')} ${targetBook.bookAuthors.map((a) => a.author.name).join(' ')}`;
    const targetVector = await AIService.generateEmbedding(targetText);

    // 3. Compute similarity scores
    const scoredCandidates = await Promise.all(
      candidates.map(async (candidate) => {
        const candidateText = `${candidate.title} ${candidate.description || ''} ${candidate.bookCategories.map((c) => c.category.name).join(' ')} ${candidate.bookAuthors.map((a) => a.author.name).join(' ')}`;
        const candidateVector = await AIService.generateEmbedding(candidateText);
        const semanticScore = AIService.cosineSimilarity(targetVector, candidateVector);

        // Boost for shared author or category
        const sharedAuthor = candidate.bookAuthors.some((a) => authorIds.includes(a.authorId)) ? 0.35 : 0;
        const sharedCategory = candidate.bookCategories.some((c) => categoryIds.includes(c.categoryId)) ? 0.25 : 0;
        const totalScore = semanticScore * 0.4 + sharedAuthor + sharedCategory;

        return {
          book: candidate,
          score: totalScore,
          semanticScore: Math.round(semanticScore * 100),
        };
      })
    );

    // Sort by highest score
    scoredCandidates.sort((a, b) => b.score - a.score);

    return scoredCandidates.slice(0, limit).map((item) => ({
      id: item.book.id,
      title: item.book.title,
      slug: item.book.slug,
      price: Number(item.book.price),
      discountPrice: item.book.discountPrice ? Number(item.book.discountPrice) : null,
      coverImage: item.book.coverImage || item.book.images[0]?.url || null,
      authorName: item.book.bookAuthors[0]?.author.name || 'Indian Author',
      categoryName: item.book.bookCategories[0]?.category.name || 'General',
      format: item.book.format,
      ratingAverage: Number(item.book.ratingAverage),
      similarityScore: item.semanticScore,
      recommendationReason: `Recommended based on thematic synergy with “${targetBook.title.slice(0, 30)}...”`,
    }));
  }

  /**
   * Personalized User Recommendations based on Cart, Wishlist, and Order history
   */
  static async getPersonalizedRecommendations(userId: string, limit = 8) {
    // 1. Fetch user's interactions
    const [cart, wishlist, orders] = await Promise.all([
      prisma.shoppingCart.findUnique({
        where: { userId },
        include: { items: { include: { book: { include: { bookCategories: { include: { category: true } } } } } } },
      }),
      prisma.wishlist.findUnique({
        where: { userId },
        include: { items: { include: { book: { include: { bookCategories: { include: { category: true } } } } } } },
      }),
      prisma.order.findMany({
        where: { userId },
        include: { items: { include: { book: { include: { bookCategories: { include: { category: true } } } } } } },
        take: 5,
      }),
    ]);

    const userBookIds = new Set<string>();
    const interactedSubjects: string[] = [];

    cart?.items.forEach((i) => {
      userBookIds.add(i.bookId);
      i.book.bookCategories.forEach((c) => interactedSubjects.push(c.category.name));
    });
    wishlist?.items.forEach((i) => {
      userBookIds.add(i.bookId);
      i.book.bookCategories.forEach((c) => interactedSubjects.push(c.category.name));
    });
    orders.forEach((o) =>
      o.items.forEach((i) => {
        userBookIds.add(i.bookId);
        i.book.bookCategories.forEach((c) => interactedSubjects.push(c.category.name));
      })
    );

    // If new user with no history, return top curated trending books
    if (userBookIds.size === 0) {
      return this.getTrendingAIRecommendations(limit);
    }

    // 2. Fetch catalog books excluding already interacted books
    const availableBooks = await prisma.book.findMany({
      where: {
        id: { notIn: Array.from(userBookIds) },
        isActive: true,
      },
      include: {
        bookAuthors: { include: { author: true } },
        bookCategories: { include: { category: true } },
        images: { where: { isPrimary: true }, take: 1 },
      },
      take: 40,
      orderBy: [{ isFeatured: 'desc' }, { ratingAverage: 'desc' }],
    });

    const userProfileText = interactedSubjects.join(' ') || 'Classical Indian Philosophy Sanskrit Literature';
    const profileVector = await AIService.generateEmbedding(userProfileText);

    const scored = await Promise.all(
      availableBooks.map(async (book) => {
        const text = `${book.title} ${book.description || ''} ${book.bookCategories.map((c) => c.category.name).join(' ')}`;
        const vector = await AIService.generateEmbedding(text);
        const score = AIService.cosineSimilarity(profileVector, vector);
        return { book, score };
      })
    );

    scored.sort((a, b) => b.score - a.score);

    return scored.slice(0, limit).map((item) => ({
      id: item.book.id,
      title: item.book.title,
      slug: item.book.slug,
      price: Number(item.book.price),
      discountPrice: item.book.discountPrice ? Number(item.book.discountPrice) : null,
      coverImage: item.book.coverImage || item.book.images[0]?.url || null,
      authorName: item.book.bookAuthors[0]?.author.name || 'Indian Author',
      categoryName: item.book.bookCategories[0]?.category.name || 'General',
      format: item.book.format,
      ratingAverage: Number(item.book.ratingAverage),
      recommendationReason: 'Personalized for your reading tastes in classical Indian literature',
    }));
  }

  /**
   * Trending AI Curated Recommendations
   */
  static async getTrendingAIRecommendations(limit = 8) {
    const books = await prisma.book.findMany({
      where: { isActive: true },
      include: {
        bookAuthors: { include: { author: true } },
        bookCategories: { include: { category: true } },
        images: { where: { isPrimary: true }, take: 1 },
      },
      take: limit,
      orderBy: [{ isBestseller: 'desc' }, { ratingAverage: 'desc' }, { createdAt: 'desc' }],
    });

    return books.map((book) => ({
      id: book.id,
      title: book.title,
      slug: book.slug,
      price: Number(book.price),
      discountPrice: book.discountPrice ? Number(book.discountPrice) : null,
      coverImage: book.coverImage || book.images[0]?.url || null,
      authorName: book.bookAuthors[0]?.author.name || 'Indian Author',
      categoryName: book.bookCategories[0]?.category.name || 'General',
      format: book.format,
      ratingAverage: Number(book.ratingAverage),
      recommendationReason: 'Trending Bestseller in Indian Literature & Scriptures',
    }));
  }
}
