import { prisma } from '../../config/database';
import { redis } from '../../config/redis';
import { siteSettingsService } from '../settings/settings.service';
import { env } from '../../config/env';

export interface SearchResult {
  type: 'campaign' | 'shift' | 'page' | 'video' | 'event';
  id: string;
  title: string;
  description: string | null;
  link: string;
}

const CACHE_PREFIX = 'search:';
const CACHE_TTL = 60; // 60 seconds

export async function search(query: string, limit = 5, isAuthenticated = false): Promise<SearchResult[]> {
  const q = query.trim().toLowerCase();
  if (!q || q.length < 2) return [];

  // Check cache
  const cacheKey = `${CACHE_PREFIX}${q}:${limit}`;
  try {
    const cached = await redis.get(cacheKey);
    if (cached) return JSON.parse(cached);
  } catch { /* cache miss */ }

  const settings = await siteSettingsService.getPublic();
  const results: SearchResult[] = [];

  // Fan-out queries (only enabled modules)
  const promises: Array<Promise<SearchResult[]>> = [];

  if (settings.enableInfluence !== false) {
    promises.push(searchCampaigns(q, limit));
  }
  if (settings.enableMap !== false && isAuthenticated) {
    promises.push(searchShifts(q, limit));
  }
  if (settings.enableLandingPages !== false) {
    promises.push(searchPages(q, limit));
  }
  if (settings.enableMediaFeatures !== false) {
    promises.push(searchMedia(q, limit));
  }

  const settled = await Promise.allSettled(promises);
  for (const result of settled) {
    if (result.status === 'fulfilled') {
      results.push(...result.value);
    }
  }

  // Sort by relevance (title exact match first) and limit total
  results.sort((a, b) => {
    const aExact = a.title.toLowerCase().includes(q) ? 0 : 1;
    const bExact = b.title.toLowerCase().includes(q) ? 0 : 1;
    return aExact - bExact;
  });
  const limited = results.slice(0, limit * 2); // Allow more total results

  // Cache
  try {
    await redis.setex(cacheKey, CACHE_TTL, JSON.stringify(limited));
  } catch { /* non-critical */ }

  return limited;
}

async function searchCampaigns(q: string, limit: number): Promise<SearchResult[]> {
  const campaigns = await prisma.campaign.findMany({
    where: {
      status: 'ACTIVE',
      OR: [
        { title: { contains: q, mode: 'insensitive' } },
        { description: { contains: q, mode: 'insensitive' } },
      ],
    },
    select: { id: true, slug: true, title: true, description: true },
    take: limit,
    orderBy: { createdAt: 'desc' },
  });
  return campaigns.map(c => ({
    type: 'campaign' as const,
    id: c.id,
    title: c.title,
    description: c.description?.slice(0, 120) ?? null,
    link: `/campaign/${c.slug}`,
  }));
}

async function searchShifts(q: string, limit: number): Promise<SearchResult[]> {
  const shifts = await prisma.shift.findMany({
    where: {
      status: 'OPEN',
      date: { gte: new Date() },
      OR: [
        { title: { contains: q, mode: 'insensitive' } },
        { location: { contains: q, mode: 'insensitive' } },
      ],
    },
    select: { id: true, title: true, location: true, date: true },
    take: limit,
    orderBy: { date: 'asc' },
  });
  return shifts.map(s => ({
    type: 'shift' as const,
    id: s.id,
    title: s.title,
    description: s.location,
    link: '/shifts',
  }));
}

async function searchPages(q: string, limit: number): Promise<SearchResult[]> {
  const pages = await prisma.landingPage.findMany({
    where: {
      published: true,
      OR: [
        { title: { contains: q, mode: 'insensitive' } },
        { description: { contains: q, mode: 'insensitive' } },
      ],
    },
    select: { slug: true, title: true, description: true },
    take: limit,
    orderBy: { updatedAt: 'desc' },
  });
  return pages.map(p => ({
    type: 'page' as const,
    id: p.slug,
    title: p.title,
    description: p.description?.slice(0, 120) ?? null,
    link: `/p/${p.slug}`,
  }));
}

async function searchMedia(q: string, limit: number): Promise<SearchResult[]> {
  try {
    const videos = await prisma.video.findMany({
      where: {
        isPublished: true,
        title: { contains: q, mode: 'insensitive' },
      },
      select: { id: true, title: true },
      take: limit,
      orderBy: { publishedAt: 'desc' },
    });
    return videos.map(v => ({
      type: 'video' as const,
      id: String(v.id),
      title: v.title ?? 'Untitled video',
      description: null,
      link: `/gallery/watch/${v.id}`,
    }));
  } catch {
    return []; // Video model might not exist
  }
}
