/**
 * N8N Knowledge Base RAG Query API
 *
 * Called by N8N workflows to get relevant context from user's knowledge base.
 * Performs semantic search across FAQs and documents.
 *
 * Returns matched FAQs, document excerpts, and custom instructions
 * to provide context for AI agents.
 */

import { NextRequest, NextResponse } from "next/server";
import { convexQuery } from "@/lib/convex-client";
import { logger } from "@/lib/logger";
import { systemSettingsService } from "@/lib/services/system-settings.service";
import { PineconeMultimodalService } from "@/lib/services/pinecone-multimodal.service";
import { z } from "zod";

// Request validation schema
const queryRequestSchema = z.object({
  auth_token: z.string().min(1),
  user_id: z.string().min(1),
  query: z.string().min(1).max(1000),
  language: z.enum(["en", "ar"]).default("ar"),
  max_results: z.number().min(1).max(20).default(5),
  include: z
    .object({
      faqs: z.boolean().default(true),
      documents: z.boolean().default(true),
      custom_instructions: z.boolean().default(true),
    })
    .default({}),
});

interface KBQueryResponse {
  success: boolean;
  results?: {
    faqs: Array<{
      question: string;
      answer: string;
      category: string;
      relevance: number;
    }>;
    documents: Array<{
      title: string;
      excerpt: string;
      document_id: string;
      relevance: number;
    }>;
    custom_instructions: string | null;
    welcome_message: string | null;
    total_matches: number;
  };
  error?: string;
}

export async function POST(request: NextRequest) {
  try {
    // Parse and validate request body
    const body = await request.json();
    const validationResult = queryRequestSchema.safeParse(body);

    if (!validationResult.success) {
      return NextResponse.json<KBQueryResponse>(
        {
          success: false,
          error: "Invalid request: " + validationResult.error.errors[0].message,
        },
        { status: 400 },
      );
    }

    const { auth_token, user_id, query, language, max_results, include } =
      validationResult.data;

    // Validate auth token (from database or env fallback)
    const isValidToken =
      await systemSettingsService.validateN8NToken(auth_token);
    if (!isValidToken) {
      logger.warn("N8N KB query - invalid auth token", { user_id });
      return NextResponse.json<KBQueryResponse>(
        { success: false, error: "Invalid authentication token" },
        { status: 401 },
      );
    }

    // Verify user has knowledge base enabled (KB settings are keyed by userId)
    const kbSettings = await convexQuery<{
      _id: string;
      isEnabled: boolean;
      customInstructions?: string;
      welcomeMessage?: string;
    }>("voice_agents/queries:getKBSettingsByUserId", { userId: user_id });

    if (!kbSettings) {
      return NextResponse.json<KBQueryResponse>(
        { success: false, error: "User not found or inactive" },
        { status: 404 },
      );
    }

    if (!kbSettings.isEnabled) {
      return NextResponse.json<KBQueryResponse>(
        { success: false, error: "Knowledge base not enabled" },
        { status: 403 },
      );
    }

    // ── Pinecone multimodal (primary when configured) ────────────────
    if (PineconeMultimodalService.isConfigured()) {
      try {
        const result = await PineconeMultimodalService.queryAndGenerate(
          user_id,
          query,
          {
            language: language as "en" | "ar",
            topK: 5,
          },
        );

        return NextResponse.json({
          success: true,
          answer: result.answer,
          sources: result.citations,
          mediaResults: result.mediaResults || [],
          metadata: {
            ...result.metadata,
            ragEngine: "pinecone-multimodal",
          },
        });
      } catch (pineconeError) {
        logger.warn("Pinecone query failed, falling back to legacy KB", {
          error: pineconeError,
          user_id,
        });
        // Fall through to keyword search
      }
    }

    // Search for matching FAQs
    const matchedFaqs: KBQueryResponse["results"]["faqs"] = [];
    if (include.faqs) {
      const faqs = await searchFAQs(user_id, query, language, max_results);
      matchedFaqs.push(...faqs);
    }

    // Search for matching documents
    const matchedDocs: KBQueryResponse["results"]["documents"] = [];
    if (include.documents) {
      const docs = await searchDocuments(user_id, query, max_results);
      matchedDocs.push(...docs);
    }

    logger.info("N8N KB query completed", {
      user_id,
      query_length: query.length,
      faq_matches: matchedFaqs.length,
      doc_matches: matchedDocs.length,
    });

    return NextResponse.json<KBQueryResponse>({
      success: true,
      results: {
        faqs: matchedFaqs,
        documents: matchedDocs,
        custom_instructions: include.custom_instructions
          ? kbSettings.customInstructions
          : null,
        welcome_message: kbSettings.welcomeMessage,
        total_matches: matchedFaqs.length + matchedDocs.length,
      },
    });
  } catch (error) {
    logger.error("N8N KB query error", { error });
    return NextResponse.json<KBQueryResponse>(
      { success: false, error: "Internal server error" },
      { status: 500 },
    );
  }
}

/**
 * Search FAQs using keyword matching
 * In production, replace with vector/embedding search for better semantic matching
 */
async function searchFAQs(
  userId: string,
  query: string,
  language: "en" | "ar",
  maxResults: number,
): Promise<KBQueryResponse["results"]["faqs"]> {
  try {
    // Normalize query for search
    const searchTerms = query
      .toLowerCase()
      .split(/\s+/)
      .filter((term) => term.length > 2);

    // Get all active FAQs for user from Convex
    const rawFaqs = await convexQuery<Array<{
      _id: string;
      questionEn: string;
      questionAr?: string;
      answerEn: string;
      answerAr?: string;
      category: string;
      priority: number;
      isActive: boolean;
    }>>("voice_agents/queries:getKBFAQs", { userId, isActive: true });
    const faqs = (rawFaqs ?? []).sort((a, b) => b.priority - a.priority);

    // Score each FAQ based on keyword matches
    const scoredFaqs = faqs.map((faq) => {
      const question =
        language === "ar" && faq.questionAr ? faq.questionAr : faq.questionEn;
      const answer =
        language === "ar" && faq.answerAr ? faq.answerAr : faq.answerEn;

      const searchText = `${question} ${answer}`.toLowerCase();

      // Calculate relevance score
      let score = 0;
      searchTerms.forEach((term) => {
        if (searchText.includes(term)) {
          score += 1;
          // Bonus for question match
          if (question.toLowerCase().includes(term)) {
            score += 0.5;
          }
        }
      });

      // Normalize by number of search terms
      const relevance = searchTerms.length > 0 ? score / searchTerms.length : 0;

      return {
        question,
        answer,
        category: faq.category,
        relevance,
      };
    });

    // Filter and sort by relevance
    return scoredFaqs
      .filter((faq) => faq.relevance > 0.3)
      .sort((a, b) => b.relevance - a.relevance)
      .slice(0, maxResults);
  } catch (error) {
    logger.error("FAQ search error", { error, userId });
    return [];
  }
}

/**
 * Search documents
 * In production, integrate with vector database or Google File Search
 */
async function searchDocuments(
  userId: string,
  query: string,
  maxResults: number,
): Promise<KBQueryResponse["results"]["documents"]> {
  try {
    // Get completed documents from Convex
    const rawDocs = await convexQuery<Array<{
      _id: string;
      prismaId?: string;
      filename: string;
      originalName: string;
      extractedText?: string;
      status: string;
    }>>("voice_agents/queries:getKBDocuments", { userId, status: "COMPLETED" });
    // Convex returns _id, adapt and limit to 50
    const documents = (rawDocs ?? []).slice(0, 50).map((d) => ({
      id: d.prismaId ?? d._id,
      filename: d.filename,
      originalName: d.originalName,
      extractedText: (d as Record<string, unknown>).extractedText as string | undefined,
    }));

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

    const searchTerms = query
      .toLowerCase()
      .split(/\s+/)
      .filter((term) => term.length > 2);

    // Score documents
    const scoredDocs = documents
      .map((doc) => {
        const text = (doc.extractedText || "").toLowerCase();
        const title = doc.originalName.toLowerCase();

        let score = 0;
        let excerpt = "";

        searchTerms.forEach((term) => {
          const termIndex = text.indexOf(term);
          if (termIndex !== -1) {
            score += 1;
            // Extract excerpt around match
            if (!excerpt) {
              const start = Math.max(0, termIndex - 100);
              const end = Math.min(text.length, termIndex + 200);
              excerpt = text.substring(start, end).trim();
              if (start > 0) excerpt = "..." + excerpt;
              if (end < text.length) excerpt = excerpt + "...";
            }
          }
          if (title.includes(term)) {
            score += 0.5;
          }
        });

        return {
          title: doc.originalName,
          excerpt: excerpt || "No relevant excerpt found",
          document_id: doc.id,
          relevance: searchTerms.length > 0 ? score / searchTerms.length : 0,
        };
      })
      .filter((doc) => doc.relevance > 0.2);

    return scoredDocs
      .sort((a, b) => b.relevance - a.relevance)
      .slice(0, maxResults);
  } catch (error) {
    logger.error("Document search error", { error, userId });
    return [];
  }
}

// Only allow POST method
export async function GET() {
  return NextResponse.json({ error: "Method not allowed" }, { status: 405 });
}
