import { NextResponse } from "next/server";
import { createClient } from "@supabase/supabase-js";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const SUPABASE_ANON = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;
const SCHEMA = process.env.NEXT_PUBLIC_SUPABASE_SCHEMA || "av";

function client(schema = SCHEMA) {
  return createClient(SUPABASE_URL, SUPABASE_ANON, {
    db: { schema: schema as "public" },
    auth: { persistSession: false },
  });
}

// Extract a short snippet around the first match of `needle` inside the
// (HTML-stripped) `haystack`. Returns the substring + the absolute offset
// of the match within the snippet, so the caller can highlight it.
function buildSnippet(
  haystack: string,
  needle: string,
  radius = 80,
): { text: string; matchStart: number; matchEnd: number } | null {
  if (!haystack || !needle) return null;
  const stripped = haystack
    .replace(/<[^>]+>/g, " ")
    .replace(/&nbsp;/g, " ")
    .replace(/&amp;/g, "&")
    .replace(/&#39;/g, "'")
    .replace(/&quot;/g, '"')
    .replace(/\s+/g, " ")
    .trim();
  const idx = stripped.toLowerCase().indexOf(needle.toLowerCase());
  if (idx === -1) return null;
  const start = Math.max(0, idx - radius);
  const end = Math.min(stripped.length, idx + needle.length + radius);
  const prefix = start > 0 ? "… " : "";
  const suffix = end < stripped.length ? " …" : "";
  const text = prefix + stripped.slice(start, end) + suffix;
  return {
    text,
    matchStart: prefix.length + (idx - start),
    matchEnd: prefix.length + (idx - start) + needle.length,
  };
}

export async function GET(req: Request) {
  const { searchParams } = new URL(req.url);
  const q = (searchParams.get("q") || "").trim();
  if (q.length < 2) {
    return NextResponse.json({ items: [], companies: [] });
  }
  const limit = Math.min(parseInt(searchParams.get("limit") || "8", 10) || 8, 15);
  const like = `%${q.replace(/[%_]/g, " ")}%`;

  // Parallel: matching directory companies (sponsors first).
  const companiesPromise = (async () => {
    try {
      const sb = client();
      const { data } = await sb
        .from("tracked_companies")
        .select("slug, company_name, logo_url, exhibits_nab, exhibits_ibc")
        .eq("directory_visible", true)
        .ilike("company_name", like)
        .order("featured", { ascending: false })
        .order("mention_count", { ascending: false })
        .limit(6);
      if (!data) return [];

      let sponsors = new Set<string>();
      try {
        const adv = client("adv");
        const { data: ads } = await adv
          .from("active_advertisers_by_property")
          .select("company_name_lower")
          .eq("property", "avbeat");
        sponsors = new Set((ads || []).map((r: any) => String(r.company_name_lower)));
      } catch { /* tolerate */ }

      const rows = (data as any[]).map((r) => ({
        slug: r.slug as string,
        name: r.company_name as string,
        logoUrl: (r.logo_url as string) || null,
        exhibitsNab: !!r.exhibits_nab,
        exhibitsIbc: !!r.exhibits_ibc,
        isSponsor: sponsors.has(String(r.company_name || "").toLowerCase()),
        href: `/manufacturers/${r.slug}`,
      }));
      rows.sort((a, b) => (b.isSponsor ? 1 : 0) - (a.isSponsor ? 1 : 0));
      return rows;
    } catch {
      return [];
    }
  })();

  const sb = client();
  // Match against title, excerpt, content, AND author name (was just
  // title+author). Content match is what surfaces hits when the phrase
  // lives deep in the article body.
  const { data, error } = await sb
    .from("wp_imported_posts")
    .select("wp_slug, title, excerpt, content, category, wp_published_at, author_name")
    .eq("status", "published")
    .or(
      [
        `title.ilike.${like}`,
        `excerpt.ilike.${like}`,
        `content.ilike.${like}`,
        `author_name.ilike.${like}`,
      ].join(","),
    )
    .order("wp_published_at", { ascending: false, nullsFirst: false })
    .limit(limit);

  if (error) {
    return NextResponse.json({ items: [], error: error.message }, { status: 500 });
  }

  // Build each item with title, category, date, and a snippet that
  // shows WHERE the match happened. Snippet source priority:
  // title → excerpt → content → author_name.
  const items = (data || []).map((r: any) => {
    let snippet: { text: string; matchStart: number; matchEnd: number } | null = null;
    let matchedIn: "title" | "excerpt" | "content" | "author" | null = null;

    const titleHit = buildSnippet(r.title || "", q, 30);
    if (titleHit) {
      snippet = titleHit;
      matchedIn = "title";
    } else {
      const exHit = buildSnippet(r.excerpt || "", q, 70);
      if (exHit) {
        snippet = exHit;
        matchedIn = "excerpt";
      } else {
        const ctHit = buildSnippet(r.content || "", q, 80);
        if (ctHit) {
          snippet = ctHit;
          matchedIn = "content";
        } else if ((r.author_name || "").toLowerCase().includes(q.toLowerCase())) {
          const a = r.author_name || "";
          const i = a.toLowerCase().indexOf(q.toLowerCase());
          snippet = { text: a, matchStart: i, matchEnd: i + q.length };
          matchedIn = "author";
        }
      }
    }

    return {
      slug: r.wp_slug,
      title: r.title,
      category: r.category,
      date: r.wp_published_at
        ? new Date(r.wp_published_at).toLocaleDateString("en-US", {
            month: "short",
            day: "numeric",
            year: "numeric",
          })
        : null,
      author_name: r.author_name || null,
      snippet,
      matched_in: matchedIn,
      href: `/news/${r.wp_slug}`,
    };
  });

  const companies = await companiesPromise;

  return NextResponse.json(
    { items, companies, query: q },
    {
      headers: {
        "Cache-Control": "public, s-maxage=30, stale-while-revalidate=60",
      },
    },
  );
}
