import { Injectable, OnModuleInit, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PrismaService } from '@chatapp/database';
import { MeiliSearch } from 'meilisearch';

@Injectable()
export class SearchService implements OnModuleInit {
  private readonly logger = new Logger(SearchService.name);
  private meili: MeiliSearch;
  private readonly indexName = 'messages';

  constructor(
    private readonly config: ConfigService,
    private readonly prisma: PrismaService,
  ) {
    this.meili = new MeiliSearch({
      host: this.config.get<string>('MEILI_URL') || 'http://localhost:7700',
      apiKey: this.config.get<string>('MEILI_MASTER_KEY') || '',
    });
  }

  async onModuleInit() {
    try {
      await this.ensureIndex();
      this.logger.log('Meilisearch index ready');
    } catch (err) {
      this.logger.error(`Failed to init Meilisearch: ${err}`);
    }
  }

  private async ensureIndex() {
    const indexes = await this.meili.getIndexes();
    const exists = indexes.results.some((i: any) => i.uid === this.indexName);
    if (!exists) {
      await this.meili.createIndex(this.indexName, { primaryKey: 'id' });
      await this.meili.index(this.indexName).updateSearchableAttributes([
        'content',
        'senderName',
        'channelName',
      ]);
      await this.meili.index(this.indexName).updateFilterableAttributes([
        'workspaceId',
        'channelId',
        'senderId',
      ]);
      this.logger.log(`Created index '${this.indexName}'`);
    }
  }

  async indexMessage(msg: any) {
    try {
      await this.meili.index(this.indexName).addDocuments([{
        id: msg.id,
        content: msg.content,
        channelId: msg.channelId,
        channelName: msg.channelName || '',
        senderId: msg.senderId,
        senderName: msg.senderName || '',
        workspaceId: msg.workspaceId,
        createdAt: msg.createdAt,
      }]);
    } catch (err) {
      this.logger.error(`Failed to index message: ${err}`);
    }
  }

  async search(workspaceId: string, query: string) {
    const start = Date.now();

    // Search messages in Meilisearch
    let messageResults: any[] = [];
    try {
      const res = await this.meili.index(this.indexName).search(query, {
        filter: [`workspaceId = "${workspaceId}"`],
        limit: 20,
      });
      messageResults = (res.hits as any[]) || [];
    } catch (err) {
      this.logger.warn(`Meilisearch query failed, falling back to DB: ${err}`);
      // Fallback: search directly in the database
      messageResults = await this.prisma.message.findMany({
        where: {
          text: { contains: query, mode: 'insensitive' },
          channel: { workspaceId },
          isDeleted: false,
        },
        take: 20,
        orderBy: { createdAt: 'desc' },
        include: { channel: true, user: true },
      }) as any[];
    }

    // Search channels in DB
    const channelResults = await this.prisma.channel.findMany({
      where: {
        workspaceId,
        name: { contains: query, mode: 'insensitive' },
        isArchived: false,
      },
      take: 10,
    });

    // Search users in DB
    const userResults = await this.prisma.user.findMany({
      where: {
        workspaceMembers: { some: { workspaceId } },
        OR: [
          { displayName: { contains: query, mode: 'insensitive' } },
          { email: { contains: query, mode: 'insensitive' } },
        ],
        isDeleted: false,
      },
      take: 10,
      select: {
        id: true,
        displayName: true,
        email: true,
        avatarUrl: true,
        title: true,
      },
    });

    // Search files in DB
    const fileResults = await this.prisma.file.findMany({
      where: {
        workspaceId,
        filename: { contains: query, mode: 'insensitive' },
        isDeleted: false,
      },
      take: 10,
      select: {
        id: true,
        filename: true,
        fileType: true,
        mimeType: true,
        sizeBytes: true,
        createdAt: true,
      },
    });

    const tookMs = Date.now() - start;

    return {
      query,
      results: {
        messages: messageResults.map((m: any) => ({
          id: m.id,
          content: m.content,
          channelId: m.channelId,
          channelName: m.channelName,
          senderId: m.senderId,
          senderName: m.senderName,
          createdAt: m.createdAt,
        })),
        channels: channelResults.map((c) => ({
          id: c.id,
          name: c.name,
          type: c.type,
        })),
        people: userResults.map((u) => ({
          id: u.id,
          displayName: u.displayName,
          email: u.email,
          avatarUrl: u.avatarUrl,
          title: u.title,
        })),
        files: fileResults.map((f) => ({
          id: f.id,
          filename: f.filename,
          fileType: f.fileType,
          mimeType: f.mimeType,
          size: Number(f.sizeBytes),
        })),
      },
      total: messageResults.length + channelResults.length + userResults.length + fileResults.length,
      took_ms: tookMs,
    };
  }
}