import { Injectable } from '@nestjs/common';
import { PrismaService } from '@core/database/prisma/prisma.service';
import * as crypto from 'crypto';

export type SearchSuggestionSource = 'RECENT_SEARCH' | 'TOP_QUERY' | 'TITLE';

export interface SearchSuggestionItem {
  text: string;
  source: SearchSuggestionSource;
}

@Injectable()
export class SearchSuggestionService {
  constructor(private readonly prisma: PrismaService) {}

  async suggest(
    userId: string,
    query: string | undefined,
    limit: number = 8,
  ): Promise<SearchSuggestionItem[]> {
    const normalized = (query ?? '').trim();
    if (normalized.length < 2) {
      return [];
    }

    const parsedLimit = Number.isFinite(limit) ? limit : 8;
    const safeLimit = Math.min(Math.max(parsedLimit, 1), 20);
    const candidateLimit = Math.max(safeLimit * 3, 20);

    const [recentQueries, topQueries, articleTitles, documentTitles] =
      await Promise.all([
        this.findRecentQueries(userId, normalized, candidateLimit),
        this.findTopQueries(normalized, candidateLimit),
        this.findArticleTitles(normalized, candidateLimit),
        this.findDocumentTitles(normalized, candidateLimit),
      ]);

    return this.mergeSuggestions(
      [...articleTitles, ...documentTitles],
      recentQueries,
      topQueries,
      safeLimit,
    );
  }

  async removeRecentSearch(userId: string, query: string): Promise<number> {
    const normalized = query.trim();
    if (!normalized) {
      return 0;
    }

    const userIdHash = this.hashUserId(userId);
    const result = await this.prisma.searchLog.deleteMany({
      where: {
        userIdHash,
        query: { equals: normalized, mode: 'insensitive' },
      },
    });
    return result.count;
  }

  async clearRecentSearches(userId: string): Promise<number> {
    const userIdHash = this.hashUserId(userId);
    const result = await this.prisma.searchLog.deleteMany({
      where: { userIdHash },
    });
    return result.count;
  }

  private async findRecentQueries(
    userId: string,
    query: string,
    limit: number,
  ): Promise<string[]> {
    const userIdHash = this.hashUserId(userId);
    const rows = await this.prisma.searchLog.findMany({
      where: {
        userIdHash,
        query: { contains: query, mode: 'insensitive' },
      },
      orderBy: { createdAt: 'desc' },
      select: { query: true },
      take: limit,
    });

    return rows.map((row) => row.query);
  }

  private async findTopQueries(
    query: string,
    limit: number,
  ): Promise<string[]> {
    const rows = await this.prisma.$queryRaw<
      Array<{ query: string; count: bigint }>
    >`
      SELECT query, COUNT(*) as count
      FROM platform_knowledge.search_logs
      WHERE query ILIKE ${`%${query}%`}
      GROUP BY query
      ORDER BY count DESC, MAX(created_at) DESC
      LIMIT ${limit}
    `;

    return rows.map((row) => row.query);
  }

  private async findArticleTitles(
    query: string,
    limit: number,
  ): Promise<string[]> {
    const rows = await this.prisma.knowledgeArticle.findMany({
      where: {
        deletedAt: null,
        title: { contains: query, mode: 'insensitive' },
      },
      orderBy: { updatedAt: 'desc' },
      select: { title: true },
      take: limit,
    });

    return rows.map((row) => row.title);
  }

  private async findDocumentTitles(
    query: string,
    limit: number,
  ): Promise<string[]> {
    const rows = await this.prisma.sPDocumentIndex.findMany({
      where: {
        title: { contains: query, mode: 'insensitive' },
      },
      orderBy: { spModifiedAt: 'desc' },
      select: { title: true },
      take: limit,
    });

    return rows.map((row) => row.title);
  }

  private mergeSuggestions(
    titles: string[],
    recentQueries: string[],
    topQueries: string[],
    limit: number,
  ): SearchSuggestionItem[] {
    const merged: SearchSuggestionItem[] = [];
    const seen = new Set<string>();

    const append = (text: string, source: SearchSuggestionSource) => {
      const normalized = text.trim();
      if (!normalized) return;
      const key = normalized.toLowerCase();
      if (seen.has(key)) return;
      seen.add(key);
      merged.push({ text: normalized, source });
    };

    titles.forEach((title) => append(title, 'TITLE'));
    recentQueries.forEach((query) => append(query, 'RECENT_SEARCH'));
    topQueries.forEach((query) => append(query, 'TOP_QUERY'));

    return merged.slice(0, limit);
  }

  private hashUserId(userId: string): string {
    return crypto.createHash('sha256').update(userId).digest('hex');
  }
}
