import { NextRequest, NextResponse } from "next/server";
import { requireFamily } from "@/lib/auth";
import { sql } from "@/db";
import OpenAI from "openai";

const EMBEDDING_MODEL = process.env.EMBEDDING_MODEL || "text-embedding-3-small";
const LITELLM_URL = process.env.LITELLM_BASE_URL;
const LITELLM_KEY = process.env.LITELLM_API_KEY;

function getBaseUrl() {
  return process.env.R2_PUBLIC_URL || `https://pub-${process.env.R2_ACCOUNT_ID}.r2.dev`;
}

// POST /api/memories/search
export async function POST(req: NextRequest) {
  const auth = await requireFamily();
  if (!auth.success) return NextResponse.json({ error: auth.error }, { status: auth.status });

  const familyId = auth.session!.familyId!;
  const body = await req.json();
  const { query, childId, limit = 20 } = body as { query: string; childId?: string; limit?: number };

  if (!query?.trim()) return NextResponse.json({ error: "query required" }, { status: 400 });

  if (!LITELLM_KEY) {
    return NextResponse.json({ error: "AI not configured" }, { status: 503 });
  }

  // Embed the query
  const openai = new OpenAI({
    apiKey: LITELLM_KEY,
    baseURL: process.env.OPENAI_API_KEY ? undefined : `${LITELLM_URL}/v1`,
  });

  let embedding: number[];
  try {
    const res = await openai.embeddings.create({ model: EMBEDDING_MODEL, input: query.trim() });
    embedding = res.data[0].embedding;
  } catch (e) {
    console.error("[search] embedding failed:", e);
    // Fall back to text search on captions/tags
    const textResults = childId
      ? await sql`
          SELECT * FROM memories
          WHERE family_id = ${familyId} AND child_id = ${childId}
            AND processing_status = 'ready'
            AND (vision_caption ILIKE ${"%" + query + "%"} OR ${query} = ANY(vision_tags))
          ORDER BY created_at DESC LIMIT ${Math.min(limit, 50)}`
      : await sql`
          SELECT * FROM memories
          WHERE family_id = ${familyId}
            AND processing_status = 'ready'
            AND (vision_caption ILIKE ${"%" + query + "%"} OR ${query} = ANY(vision_tags))
          ORDER BY created_at DESC LIMIT ${Math.min(limit, 50)}`;

    return NextResponse.json({ items: formatResults(textResults) });
  }

  const embeddingLiteral = `[${embedding.join(",")}]`;
  const cap = Math.min(limit, 50);

  const rows = childId
    ? await sql.unsafe(
        `SELECT *, (vision_embedding <=> $1::vector) AS distance
         FROM memories
         WHERE family_id = $2 AND child_id = $3 AND vision_embedding IS NOT NULL AND processing_status = 'ready'
         ORDER BY distance ASC LIMIT $4`,
        [embeddingLiteral, familyId, childId, cap]
      )
    : await sql.unsafe(
        `SELECT *, (vision_embedding <=> $1::vector) AS distance
         FROM memories
         WHERE family_id = $2 AND vision_embedding IS NOT NULL AND processing_status = 'ready'
         ORDER BY distance ASC LIMIT $3`,
        [embeddingLiteral, familyId, cap]
      );

  // Filter out low-relevance results (cosine distance > 0.7)
  const relevant = rows.filter((r: Record<string, unknown>) => (r.distance as number) < 0.7);

  return NextResponse.json({ items: formatResults(relevant) });
}

function formatResults(rows: Record<string, unknown>[]) {
  const baseUrl = getBaseUrl();
  return rows.map(m => ({
    id: m.id,
    key: m.r2_key,
    url: `${baseUrl}/${m.r2_key}`,
    thumbnailUrl: m.r2_thumbnail_key ? `${baseUrl}/${m.r2_thumbnail_key}` : null,
    sizeBytes: m.size_bytes,
    mimeType: m.mime_type,
    title: m.title,
    description: m.description,
    takenAt: m.taken_at,
    visionCaption: m.vision_caption,
    visionTags: m.vision_tags,
    isPrivate: m.is_private,
    processingStatus: m.processing_status,
    createdAt: m.created_at,
  }));
}
