"use client";

import Link from "next/link";
import { useEffect, useState } from "react";

type Company = { name: string; slug: string; needle: string };

// Module-level cache: fetched once per page load, reused across every
// LinkifyPlainText instance on the page.
let companyListPromise: Promise<Company[]> | null = null;

function loadCompanies(): Promise<Company[]> {
  if (companyListPromise) return companyListPromise;
  companyListPromise = (async () => {
    try {
      const r = await fetch("/api/public/companies/list");
      if (!r.ok) return [];
      const j = await r.json();
      const rows: Company[] = (j.companies || []).map((c: any) => ({
        name: c.name,
        slug: c.slug,
        needle: String(c.name).toLowerCase(),
      }));
      // Longest first so "Sony Pictures" beats "Sony"
      rows.sort((a, b) => b.needle.length - a.needle.length);
      return rows;
    } catch {
      return [];
    }
  })();
  return companyListPromise;
}

function escapeRegex(s: string): string {
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

interface Segment {
  type: "text" | "link";
  text: string;
  slug?: string;
}

function buildSegments(text: string, companies: Company[], maxLinks = 10): Segment[] {
  if (!text) return [];
  if (companies.length === 0) return [{ type: "text", text }];

  const out: Segment[] = [];
  let cursor = 0;
  const seen = new Set<string>();
  let linkCount = 0;

  while (cursor < text.length && linkCount < maxLinks) {
    // Find earliest match starting at or after cursor
    let best: { start: number; end: number; company: Company } | null = null;
    for (const c of companies) {
      if (seen.has(c.slug)) continue;
      const re = new RegExp(`(^|[^A-Za-z0-9])(${escapeRegex(c.name)})(?![A-Za-z0-9])`, "i");
      re.lastIndex = 0;
      const m = re.exec(text.slice(cursor));
      if (!m) continue;
      const absStart = cursor + m.index + m[1].length;
      const absEnd = absStart + m[2].length;
      if (!best || absStart < best.start || (absStart === best.start && absEnd - absStart > best.end - best.start)) {
        best = { start: absStart, end: absEnd, company: c };
      }
    }
    if (!best) break;
    if (best.start > cursor) {
      out.push({ type: "text", text: text.slice(cursor, best.start) });
    }
    out.push({ type: "link", text: text.slice(best.start, best.end), slug: best.company.slug });
    seen.add(best.company.slug);
    linkCount++;
    cursor = best.end;
  }
  if (cursor < text.length) out.push({ type: "text", text: text.slice(cursor) });
  return out;
}

export default function LinkifyPlainText({
  body,
  className,
  preserveWhitespace = true,
}: {
  body: string;
  className?: string;
  preserveWhitespace?: boolean;
}) {
  const [companies, setCompanies] = useState<Company[] | null>(null);

  useEffect(() => {
    let alive = true;
    loadCompanies().then((c) => { if (alive) setCompanies(c); });
    return () => { alive = false; };
  }, []);

  const segments = companies ? buildSegments(body || "", companies) : null;
  const wsClass = preserveWhitespace ? "whitespace-pre-wrap" : "";
  const combined = `${wsClass} ${className || ""}`.trim();

  if (!segments) {
    // Companies still loading — render raw text so we don't FOUC.
    return <span className={combined}>{body}</span>;
  }

  return (
    <span className={combined}>
      {segments.map((s, i) =>
        s.type === "link" && s.slug ? (
          <Link
            key={i}
            href={`/manufacturers/${s.slug}`}
            data-company-slug={s.slug}
            className="company-mention"
          >
            {s.text}
          </Link>
        ) : (
          <span key={i}>{s.text}</span>
        ),
      )}
    </span>
  );
}
