"use client";

import { useState, useEffect } from "react";
import { RefreshCw, BarChart3, Sparkles, History, Trash2, Loader2, Globe, ChevronDown, ChevronUp, FileText, Server, KeyRound, ExternalLink } from "lucide-react";

interface RankedKeyword {
  keyword: string;
  position: number | null;
  url: string | null;
  volume: number | null;
  difficulty: number | null;
}

interface RankedKeywordsResponse {
  keywords: RankedKeyword[];
  totalCount: number;
  configured?: boolean;
  error?: string;
  /** Server-attached note when the response is legitimately empty
   *  (domain not indexed, no top-20 keywords, etc.) — lets the UI
   *  show a specific message instead of the generic empty state. */
  note?: string;
  /** Cache metadata from the server's 7-day SiteEvent cache. Present
   *  when the rows were served from cache; used to render a small
   *  "Cached" badge + expose the Refresh button that calls with
   *  ?fresh=1 to bypass. */
  cached?: boolean;
  cachedAgeHours?: number;
}

export interface CompetitorDto {
  id: string; domain: string; name: string; isActive: boolean;
  lastCrawledAt?: string | null;
  detectedCms?: string | null;
  snapshots: Array<{ totalPages?: number | null; newPages?: unknown; removedPages?: unknown; changedPages?: unknown; crawledAt?: string }>;
}

interface Props {
  comp: CompetitorDto;
  onCrawl: (id: string) => Promise<void>;
  onCompare: (id: string) => void;
  onAnalyze: (id: string) => void;
  onChanges: (id: string) => void;
  onDelete: (id: string) => void;
}

export default function CompetitorCard({ comp, onCrawl, onCompare, onAnalyze, onChanges, onDelete }: Props) {
  const [crawling, setCrawling] = useState(false);
  const [pageCounts, setPageCounts] = useState<Record<string, number> | null>(null);
  // Word-count aggregate across the competitor's crawled pages.
  // Populated from the same /pages fetch that loads pageCounts below,
  // so we get both for one round-trip. `null` before the fetch
  // lands; `{ avg: null, ... }` after a fetch that found zero pages
  // with wordCount. The rendering distinguishes those two states so
  // we don't flash "—" on the initial paint.
  const [wordStats, setWordStats] = useState<{
    avg: number | null;
    pagesWithContent: number;
    min: number | null;
    max: number | null;
  } | null>(null);
  // Ranked-keywords panel state. Lazy-loaded on first toggle so the
  // ~$0.05 SERP-provider call only fires when the user actually clicks.
  const [kwOpen, setKwOpen] = useState(false);
  const [kwLoading, setKwLoading] = useState(false);
  const [kwData, setKwData] = useState<RankedKeywordsResponse | null>(null);
  const snap = comp.snapshots[0];
  const totalPages = snap?.totalPages ?? 0;
  const newPages = Array.isArray(snap?.newPages) ? (snap.newPages as string[]).length : 0;
  const updatedPages = Array.isArray(snap?.changedPages) ? (snap.changedPages as string[]).length : 0;
  const removedPages = Array.isArray(snap?.removedPages) ? (snap.removedPages as string[]).length : 0;
  const lastCrawled = (comp.lastCrawledAt ?? snap?.crawledAt)
    ? new Date(comp.lastCrawledAt ?? snap!.crawledAt!).toLocaleDateString("en-US", { month: "short", day: "numeric" })
    : "Never";

  // Fetch per-status page counts + word-count aggregate from the
  // CompetitorPage table (background). Both ride on the same /pages
  // response so we don't pay a second round-trip for wordStats. Non-
  // blocking; a missing wordStats just doesn't render the card cell.
  useEffect(() => {
    let cancelled = false;
    fetch(`/api/competitors/${comp.id}/pages?limit=0`)
      .then((r) => r.ok ? r.json() : null)
      .then((d) => {
        if (cancelled || !d) return;
        if (d.statusCounts) setPageCounts(d.statusCounts);
        if (d.wordStats) setWordStats(d.wordStats);
      })
      .catch(() => {});
    return () => { cancelled = true; };
  }, [comp.id]);

  async function handleCrawl() { setCrawling(true); try { await onCrawl(comp.id); } finally { setCrawling(false); } }

  /** Load ranked keywords. Called when the panel opens + when the
   *  user explicitly clicks Refresh. `force=true` appends ?fresh=1
   *  so the server bypasses its 7-day SiteEvent cache and hits the
   *  SERP provider live — costs ~$0.05 per forced refresh, so gated
   *  behind an explicit button rather than auto-firing. */
  async function fetchKeywords(force: boolean) {
    setKwLoading(true);
    const controller = new AbortController();
    const abortTimer = setTimeout(() => controller.abort(), 30_000);
    try {
      const url = force
        ? `/api/competitors/${comp.id}/ranked-keywords?fresh=1`
        : `/api/competitors/${comp.id}/ranked-keywords`;
      const res = await fetch(url, { signal: controller.signal });
      const json = await res.json();
      if (!res.ok) {
        setKwData({ keywords: [], totalCount: 0, configured: json.configured ?? true, error: json.error });
      } else {
        setKwData(json);
      }
    } catch (err) {
      const msg = err instanceof DOMException && err.name === "AbortError"
        ? "Request timed out after 30s — search data temporarily unavailable. Try again in a minute."
        : err instanceof Error ? err.message : "Network error";
      setKwData({ keywords: [], totalCount: 0, error: msg });
    } finally {
      clearTimeout(abortTimer);
      setKwLoading(false);
    }
  }

  async function toggleKeywords() {
    if (kwOpen) { setKwOpen(false); return; }
    setKwOpen(true);
    if (kwData) return; // already fetched in this session
    // 30s client-side abort — Ranked Keywords can be slow
    // (~10-20s on cold cache), but 30s is the hard ceiling before we
    // declare the call stuck and surface an error instead of spinning.
    await fetchKeywords(false);
  }

  return (
    <div className={`bg-white rounded-xl border border-slate-200 overflow-hidden ${!comp.isActive ? "opacity-60" : ""}`}>
      <div className="px-5 py-4">
        <div className="flex items-start justify-between mb-3">
          <div className="flex items-center gap-2.5">
            <div className="w-9 h-9 rounded-lg bg-slate-100 flex items-center justify-center"><Globe className="w-4 h-4 text-slate-500" /></div>
            <div>
              <h3 className="text-sm font-bold text-slate-900">{comp.domain}</h3>
              <div className="flex items-center gap-2 mt-0.5 flex-wrap">
                {comp.name && comp.name !== comp.domain && <span className="text-[11px] text-slate-500">{comp.name}</span>}
                <span className={`text-[9px] font-bold uppercase tracking-wider rounded-full px-2 py-0.5 ${comp.isActive ? "bg-green-50 text-green-700" : "bg-slate-100 text-slate-500"}`}>{comp.isActive ? "Active" : "Inactive"}</span>
                {comp.detectedCms && (
                  <span className="text-[9px] font-bold uppercase tracking-wider rounded-full px-2 py-0.5 bg-indigo-50 text-indigo-600 border border-indigo-200 flex items-center gap-0.5">
                    <Server className="w-2.5 h-2.5" /> {comp.detectedCms}
                  </span>
                )}
                <span className="text-[10px] text-slate-400">Crawled: {lastCrawled}</span>
              </div>
            </div>
          </div>
          <button onClick={() => { if (confirm(`Remove ${comp.domain}?`)) onDelete(comp.id); }} className="p-1.5 rounded-md text-slate-400 hover:bg-red-50 hover:text-red-600"><Trash2 className="w-3.5 h-3.5" /></button>
        </div>

        <div className="grid grid-cols-4 gap-3 mb-3">
          <StatBox label="Total Pages" value={pageCounts ? Object.values(pageCounts).reduce((a, b) => a + b, 0) : totalPages} />
          <StatBox label="New" value={pageCounts?.new ?? newPages} color={(pageCounts?.new ?? newPages) > 0 ? "text-green-600" : undefined} />
          <StatBox label="Updated" value={pageCounts?.updated ?? updatedPages} color={(pageCounts?.updated ?? updatedPages) > 0 ? "text-blue-600" : undefined} />
          <StatBox label="Removed" value={pageCounts?.removed ?? removedPages} color={(pageCounts?.removed ?? removedPages) > 0 ? "text-red-500" : undefined} />
        </div>

        {/* Word-count footer. Surfaces the competitor's avg content
            depth so users can decide whether their own pages are
            under-performing on length. Only renders once wordStats
            has loaded AND there's at least one page with non-zero
            wordCount — avoids a misleading "Avg 0" reading for
            competitors whose crawl hasn't scraped page bodies yet
            (e.g. sitemap-only discovery with no DETAIL_FETCH_LIMIT
            pages hit yet). Renders `—` when loaded-but-empty so the
            layout doesn't jump when data arrives. */}
        {wordStats !== null && (
          <div className="flex items-center justify-between gap-3 pt-2 border-t border-slate-100 text-[11px] text-slate-500">
            <div className="flex items-center gap-1.5">
              <FileText className="w-3 h-3 text-slate-400" />
              <span>Avg Words:</span>
              <span className="font-semibold text-slate-700 tabular-nums">
                {wordStats.avg != null ? wordStats.avg.toLocaleString() : "—"}
              </span>
              {wordStats.pagesWithContent > 0 && (
                <span className="text-slate-400">
                  across {wordStats.pagesWithContent} page{wordStats.pagesWithContent === 1 ? "" : "s"}
                </span>
              )}
            </div>
            {wordStats.max != null && wordStats.min != null && wordStats.max !== wordStats.min && (
              <span className="text-slate-400 tabular-nums">
                range {wordStats.min.toLocaleString()}–{wordStats.max.toLocaleString()}
              </span>
            )}
          </div>
        )}
      </div>

      <div className="flex items-center gap-2 border-t border-slate-100 px-5 py-2.5 bg-slate-50/60 flex-wrap">
        <button onClick={handleCrawl} disabled={crawling} className="flex items-center gap-1 px-2.5 py-1.5 rounded-lg border border-slate-200 text-[11px] font-semibold text-slate-600 hover:bg-white disabled:opacity-50">
          {crawling ? <Loader2 className="w-3 h-3 animate-spin" /> : <RefreshCw className="w-3 h-3" />} Re-crawl
        </button>
        <button onClick={() => onCompare(comp.id)} className="flex items-center gap-1 px-2.5 py-1.5 rounded-lg border border-slate-200 text-[11px] font-semibold text-slate-600 hover:bg-white">
          <BarChart3 className="w-3 h-3" /> Compare
        </button>
        <button onClick={() => onAnalyze(comp.id)} className="flex items-center gap-1 px-2.5 py-1.5 rounded-lg border border-brand-200 bg-brand-50 text-[11px] font-semibold text-brand-700 hover:bg-brand-100">
          <Sparkles className="w-3 h-3" /> AI Analysis
        </button>
        <button onClick={toggleKeywords} className="flex items-center gap-1 px-2.5 py-1.5 rounded-lg border border-slate-200 text-[11px] font-semibold text-slate-600 hover:bg-white">
          {kwLoading ? <Loader2 className="w-3 h-3 animate-spin" /> : <KeyRound className="w-3 h-3" />}
          Top Keywords {kwOpen ? <ChevronUp className="w-3 h-3" /> : <ChevronDown className="w-3 h-3" />}
        </button>
        <button onClick={() => onChanges(comp.id)} className="flex items-center gap-1 px-2.5 py-1.5 rounded-lg border border-slate-200 text-[11px] font-semibold text-slate-600 hover:bg-white ml-auto">
          <History className="w-3 h-3" /> Changes <ChevronDown className="w-3 h-3" />
        </button>
      </div>

      {/* Ranked Keywords panel — hits /api/competitors/[id]/ranked-keywords
          on first open. Cost is roughly $0.05 per click; cached client-side
          for the rest of this card's lifetime. */}
      {kwOpen && (
        <div className="border-t border-slate-100 px-5 py-3 bg-slate-50/40">
          {kwLoading ? (
            <div className="flex items-center justify-center py-6 text-slate-400 text-xs">
              <Loader2 className="w-4 h-4 animate-spin mr-2" /> Loading top keywords…
            </div>
          ) : kwData?.configured === false ? (
            <p className="text-xs text-slate-500 py-3 text-center">Live competitor keyword data isn&apos;t configured for this workspace yet.</p>
          ) : kwData?.error ? (
            <p className="text-xs text-red-600 py-3 text-center">{kwData.error}</p>
          ) : kwData && kwData.keywords.length > 0 ? (
            <>
              <div className="flex items-center justify-between gap-2 mb-2 flex-wrap">
                <div className="flex items-center gap-2">
                  <p className="text-[10px] font-bold uppercase tracking-widest text-slate-400">
                    Top Keywords ({kwData.totalCount.toLocaleString()} ranking in top 20)
                  </p>
                  {/* Cached pill surfaces when the server returned
                      a 7-day-cached response — pairs with the
                      Refresh button so users can tell at a glance
                      whether the numbers are live or cached. */}
                  {kwData.cached && (
                    <span
                      className="inline-flex items-center gap-1 text-[9px] font-bold uppercase tracking-wide px-1.5 py-0.5 rounded border bg-slate-100 text-slate-600 border-slate-200"
                      title={`Cached ${kwData.cachedAgeHours ?? "?"}h ago. Click Refresh to fetch live SERP data.`}
                    >
                      Cached{typeof kwData.cachedAgeHours === "number" ? ` · ${kwData.cachedAgeHours}h` : ""}
                    </span>
                  )}
                </div>
                <button
                  onClick={() => fetchKeywords(true)}
                  disabled={kwLoading}
                  className="inline-flex items-center gap-1 text-[10px] font-semibold text-slate-500 hover:text-slate-700 underline underline-offset-2 disabled:opacity-40"
                  title="Force a fresh SERP fetch, bypassing the 7-day server cache"
                >
                  {kwLoading ? <Loader2 className="w-2.5 h-2.5 animate-spin" /> : <RefreshCw className="w-2.5 h-2.5" />}
                  Refresh
                </button>
              </div>
              <table className="w-full text-xs">
                <thead>
                  <tr className="text-left text-[10px] font-semibold uppercase tracking-wider text-slate-400">
                    <th className="py-1.5">Keyword</th>
                    <th className="py-1.5 text-right w-12">Pos</th>
                    <th className="py-1.5 text-right w-16">Volume</th>
                    <th className="py-1.5 w-48">URL</th>
                  </tr>
                </thead>
                <tbody className="divide-y divide-slate-100">
                  {kwData.keywords.slice(0, 10).map((k) => (
                    <tr key={k.keyword}>
                      <td className="py-1.5 text-slate-800 font-medium">{k.keyword}</td>
                      <td className="py-1.5 text-right tabular-nums font-bold text-slate-700">#{k.position ?? "—"}</td>
                      <td className="py-1.5 text-right tabular-nums text-slate-500">{k.volume?.toLocaleString() ?? "—"}</td>
                      <td className="py-1.5 truncate">
                        {k.url ? (
                          <a
                            href={`https://${comp.domain}${k.url}`}
                            target="_blank"
                            rel="noopener noreferrer"
                            className="text-[10px] text-brand-600 hover:underline inline-flex items-center gap-1"
                            title={k.url}
                          >
                            {k.url.slice(0, 38)}{k.url.length > 38 ? "…" : ""}
                            <ExternalLink className="w-2.5 h-2.5" />
                          </a>
                        ) : <span className="text-slate-300">—</span>}
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
              {kwData.totalCount > 10 && (
                <p className="text-[10px] text-slate-400 mt-2 text-center">
                  Showing top 10 of {kwData.totalCount.toLocaleString()} ranking keywords
                </p>
              )}
            </>
          ) : (
            <p className="text-xs text-slate-500 py-3 text-center">
              {kwData?.note ?? "No ranking keywords found in top 20."}
            </p>
          )}
        </div>
      )}
    </div>
  );
}

function StatBox({ label, value, color }: { label: string; value: number; color?: string }) {
  return (
    <div className="bg-slate-50 rounded-lg px-3 py-2.5 text-center">
      <p className="text-[10px] font-bold uppercase tracking-widest text-slate-400">{label}</p>
      <p className={`text-lg font-bold tabular-nums ${color || "text-slate-900"}`}>{value.toLocaleString()}</p>
    </div>
  );
}
