"use client";

/**
 * Competitor Intel — competitive intelligence dashboard.
 * Sitemap monitoring, ranking comparison, AI strategic analysis,
 * change detection timeline.
 */

import { useCallback, useEffect, useState } from "react";
import { useBrand } from "@/context/brand-context";
import { useBackgroundTasks } from "@/lib/stores/task-store";
import { Plus, Users, Loader2 } from "lucide-react";

import CompetitorCard, { type CompetitorDto } from "@/components/competitor-intel/competitor-card";
import LandscapeOverview from "@/components/competitor-intel/landscape-overview";
import RankingComparison from "@/components/competitor-intel/ranking-comparison";
import AiAnalysisPanel from "@/components/competitor-intel/ai-analysis-panel";
import ChangeTimeline from "@/components/competitor-intel/change-timeline";
import RecentActivity from "@/components/competitor-intel/recent-activity";
import ContentGaps from "@/components/competitor-intel/content-gaps";
import KeywordBattles from "@/components/competitor-intel/keyword-battles";
import DiscoverCompetitors from "@/components/competitor-intel/discover-competitors";

interface OverviewData {
  brandPages: number;
  competitors: Array<{ domain: string; pages: number; newPages: number }>;
  outrankedKeywords: Array<{ keyword: string; position: number; domain: string }>;
}

export default function CompetitorIntelPage() {
  const { brand } = useBrand();
  const { addTask, completeTask, failTask } = useBackgroundTasks();
  const [competitors, setCompetitors] = useState<CompetitorDto[]>([]);
  const [overview, setOverview] = useState<OverviewData | null>(null);
  const [loading, setLoading] = useState(true);
  const [adding, setAdding] = useState(false);
  const [domainInput, setDomainInput] = useState("");
  const [toast, setToast] = useState<string | null>(null);
  // Surfaces a failed competitors/overview load instead of leaving
  // the page on a silent empty state. Previously the catch swallowed
  // errors with `/* swallow */` and users could not tell "no
  // competitors tracked" from "API is down".
  const [loadError, setLoadError] = useState<string | null>(null);
  const [contentGapKeywords, setContentGapKeywords] = useState<Array<{
    keyword: string; competitorUrl: string; competitorDomain: string;
    wordCount: number | null; confidence: number;
  }>>([]);

  // Active panels — only one open at a time
  const [compareId, setCompareId] = useState<string | null>(null);
  const [analyzeId, setAnalyzeId] = useState<string | null>(null);
  const [changesId, setChangesId] = useState<string | null>(null);

  const show = (msg: string) => { setToast(msg); setTimeout(() => setToast(null), 2500); };

  const load = useCallback(async () => {
    if (!brand?.id) return;
    setLoading(true);
    try {
      const [compRes, ovRes] = await Promise.all([
        fetch(`/api/competitors?brandId=${brand.id}`),
        fetch(`/api/competitors/overview?brandId=${brand.id}`),
      ]);
      if (compRes.ok) {
        const d = await compRes.json();
        // Normalize: old API returns array directly or { competitors: [...] }; new API returns objects with isActive/snapshots
        const raw = Array.isArray(d) ? d : Array.isArray(d.competitors) ? d.competitors : [];
        const normalized: CompetitorDto[] = raw.map((c: Record<string, unknown>) => ({
          id: String(c.id ?? ""),
          domain: String(c.domain ?? ""),
          name: String(c.name ?? c.domain ?? ""),
          isActive: c.isActive !== false, // default true if field missing
          lastCrawledAt: typeof c.lastCrawledAt === "string" ? c.lastCrawledAt : null,
          detectedCms: typeof c.detectedCms === "string" ? c.detectedCms : null,
          snapshots: Array.isArray(c.snapshots) ? c.snapshots : [],
        }));
        setCompetitors(normalized.filter((c) => c.isActive));
      }
      if (ovRes.ok) {
        try { setOverview(await ovRes.json()); } catch { /* overview shape mismatch — ignore */ }
      }
      // Surface partial failures: if EITHER the competitors list or
      // the overview fetch returned non-2xx, raise an amber banner.
      // Both OK = clear any prior error.
      if (!compRes.ok || !ovRes.ok) {
        const parts: string[] = [];
        if (!compRes.ok) parts.push(`competitors HTTP ${compRes.status}`);
        if (!ovRes.ok) parts.push(`overview HTTP ${ovRes.status}`);
        setLoadError(`Couldn't refresh (${parts.join(", ")}). Showing last known state.`);
      } else {
        setLoadError(null);
      }
    } catch (err) {
      // Network error. Keep previously-loaded competitors visible;
      // raise a banner so the user can retry instead of seeing an
      // empty list that looks like they have no competitors.
      console.warn("[competitors] load failed", err);
      setLoadError("Couldn't reach the competitors API. Check your connection and retry.");
    }
    setLoading(false);

    // Background: fetch content gaps and extract keywords for the
    // Keyword Battles "Content Gaps" filter. Fire-and-forget so the
    // main page doesn't wait for this secondary data.
    fetch(`/api/competitors/analysis?brandId=${brand.id}`)
      .then((r) => r.ok ? r.json() : null)
      .then((analysis: { gaps?: Array<{ competitorUrl: string; competitorDomain: string; topic: string; wordCount: number | null; matchConfidence: number }> } | null) => {
        if (!analysis?.gaps?.length) return;
        const kws = analysis.gaps.map((g) => ({
          keyword: g.topic.toLowerCase(),
          competitorUrl: g.competitorUrl,
          competitorDomain: g.competitorDomain,
          wordCount: g.wordCount,
          confidence: g.matchConfidence,
        }));
        setContentGapKeywords(kws);
      })
      .catch(() => null);
  }, [brand.id]);

  useEffect(() => { load(); }, [load]);

  async function addCompetitor() {
    if (!domainInput.trim() || adding) return;
    setAdding(true);
    const taskId = addTask({ id: `comp_${Date.now()}`, type: "competitor_crawl", label: `Crawling ${domainInput.trim()}...`, brandId: brand.id, resultUrl: "/competitors" });
    try {
      const res = await fetch("/api/competitors/add", {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ brandId: brand.id, domain: domainInput.trim() }),
      });
      if (res.ok) { show("Competitor added — crawling sitemap..."); setDomainInput(""); await load(); completeTask(taskId, { resultUrl: "/competitors", resultSummary: "Competitor added and sitemap crawled" }); }
      else { const d = await res.json().catch(() => ({})); show(d.error || "Failed to add"); failTask(taskId, d.error || "Failed to add competitor"); }
    } catch { show("Failed to add competitor"); failTask(taskId, "Network error"); }
    setAdding(false);
  }

  async function crawl(id: string) {
    show("Re-crawling sitemap...");
    const taskId = addTask({ id: `crawl_${Date.now()}`, type: "competitor_crawl", label: "Re-crawling competitor sitemap...", brandId: brand.id, resultUrl: "/competitors" });
    try {
      await fetch(`/api/competitors/${id}/crawl`, { method: "POST" });
      await load();
      show("Crawl complete!");
      completeTask(taskId, { resultUrl: "/competitors", resultSummary: "Sitemap crawl complete" });
    } catch { failTask(taskId, "Crawl failed"); }
  }

  async function deleteComp(id: string) {
    await fetch(`/api/competitors/${id}`, { method: "DELETE" });
    setCompareId(null); setAnalyzeId(null); setChangesId(null);
    await load();
  }

  function findDomain(id: string): string {
    return competitors.find((c) => c.id === id)?.domain ?? "";
  }

  function openPanel(type: "compare" | "analyze" | "changes", id: string) {
    setCompareId(type === "compare" ? (compareId === id ? null : id) : null);
    setAnalyzeId(type === "analyze" ? (analyzeId === id ? null : id) : null);
    setChangesId(type === "changes" ? (changesId === id ? null : id) : null);
  }

  if (loading) return <div className="flex items-center justify-center h-64"><div className="w-8 h-8 border-4 border-brand-100 border-t-brand-600 rounded-full animate-spin" /></div>;

  // Empty state
  if (competitors.length === 0) {
    return (
      <div className="space-y-6 max-w-5xl">
        <div><h1 className="text-2xl font-bold text-slate-900">Competitor Intel</h1><p className="text-slate-500 text-sm mt-1">Monitor competitors for {brand.domain}</p></div>
        <div className="bg-white rounded-xl border border-slate-200 p-12 text-center">
          <Users className="w-14 h-14 text-slate-200 mx-auto mb-4" />
          <h2 className="text-lg font-bold text-slate-900 mb-1">Start Monitoring Competitors</h2>
          <p className="text-sm text-slate-500 max-w-md mx-auto mb-6">Add competitor domains to track their sitemap changes, compare keyword rankings, and get AI-powered strategic insights.</p>
          <div className="flex items-center gap-2 max-w-md mx-auto">
            <input value={domainInput} onChange={(e) => setDomainInput(e.target.value)} onKeyDown={(e) => e.key === "Enter" && addCompetitor()} placeholder="competitor.com" className="flex-1 px-4 py-2.5 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500/40" />
            <button onClick={addCompetitor} disabled={adding} className="flex items-center gap-1.5 px-5 py-2.5 bg-brand-600 text-white text-sm font-semibold rounded-lg hover:bg-brand-700 disabled:opacity-50">
              {adding ? <Loader2 className="w-4 h-4 animate-spin" /> : <Plus className="w-4 h-4" />} Add
            </button>
          </div>
          <div className="mt-6 max-w-2xl mx-auto text-left">
            {/* AI-powered discovery — sits below the manual entry on the
                empty-state card so the user has both options without
                losing the manual flow. */}
            <DiscoverCompetitors brandId={brand.id} onAdded={load} onMessage={show} />
          </div>
        </div>
        {toast && <div className="fixed top-4 right-4 z-50 px-4 py-2 rounded-lg bg-slate-900 text-white text-xs font-semibold shadow-lg">{toast}</div>}
      </div>
    );
  }

  return (
    <div className="space-y-6 max-w-6xl">
      {/* Header + Add */}
      <div className="flex items-start justify-between">
        <div><h1 className="text-2xl font-bold text-slate-900">Competitor Intel</h1><p className="text-slate-500 text-sm mt-1">{competitors.length} competitor{competitors.length !== 1 ? "s" : ""} tracked for {brand.domain}</p></div>
        <div className="flex items-center gap-2">
          <input value={domainInput} onChange={(e) => setDomainInput(e.target.value)} onKeyDown={(e) => e.key === "Enter" && addCompetitor()} placeholder="Add competitor domain..." className="px-3 py-2 text-sm border border-slate-200 rounded-lg w-56 focus:outline-none focus:ring-2 focus:ring-brand-500/40" />
          <button onClick={addCompetitor} disabled={adding || !domainInput.trim()} className="flex items-center gap-1.5 px-4 py-2 bg-brand-600 text-white text-xs font-semibold rounded-lg hover:bg-brand-700 disabled:opacity-50">
            {adding ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Plus className="w-3.5 h-3.5" />} Add
          </button>
        </div>
      </div>

      {/* Load-error banner. Populated when /api/competitors or
          /api/competitors?summary=true fails — previously the catch
          silently returned and the user saw an empty list that looked
          identical to "no competitors tracked". Amber + Retry so the
          distinction between empty and stale is obvious. */}
      {loadError && (
        <div className="rounded-xl px-4 py-3 text-sm bg-amber-50 text-amber-800 border border-amber-200 flex items-start justify-between gap-3">
          <span>{loadError}</span>
          <button onClick={() => load()} className="text-xs font-semibold underline shrink-0">Retry</button>
        </div>
      )}

      {/* Discovery prompt — shown when the brand has 1-2 competitors so
          they're nudged to fill out the competitive set without the
          card dominating the page once they have a healthy list. */}
      {competitors.length > 0 && competitors.length < 3 && (
        <DiscoverCompetitors brandId={brand.id} onAdded={load} onMessage={show} />
      )}

      {/* Landscape overview */}
      {overview && <LandscapeOverview brandPages={overview.brandPages ?? 0} competitors={overview.competitors ?? []} outrankedCount={overview.outrankedKeywords?.length ?? 0} />}

      {/* Competitor cards */}
      <div>
        <h2 className="text-xs font-bold uppercase tracking-widest text-slate-400 mb-3">Competitors</h2>
        <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
          {competitors.map((comp) => (
            <CompetitorCard key={comp.id} comp={comp} onCrawl={crawl}
              onCompare={(id) => openPanel("compare", id)}
              onAnalyze={(id) => openPanel("analyze", id)}
              onChanges={(id) => openPanel("changes", id)}
              onDelete={deleteComp} />
          ))}
        </div>
      </div>

      {/* Expandable panels */}
      {compareId && <RankingComparison competitorId={compareId} competitorDomain={findDomain(compareId)} onClose={() => setCompareId(null)} />}
      {analyzeId && <AiAnalysisPanel competitorId={analyzeId} competitorDomain={findDomain(analyzeId)} onClose={() => setAnalyzeId(null)} />}
      {changesId && <ChangeTimeline competitorId={changesId} competitorDomain={findDomain(changesId)} onClose={() => setChangesId(null)} />}

      {/* Keyword Battles — SERP provider's Domain Intersection per competitor */}
      <KeywordBattles
        brandId={brand.id}
        brandDomain={brand.domain}
        competitors={competitors.map((c) => ({ id: c.id, domain: c.domain, name: c.name }))}
        contentGapKeywords={contentGapKeywords}
        onTrackKeyword={(kw) => {
          // Cross-page handoff: open the Rank Tracker with the keyword
          // pre-populated. The add-keywords-modal honours `?k=` on mount
          // so the user lands on a one-click confirmation.
          window.open(`/rank-tracker?addKeyword=${encodeURIComponent(kw)}`, "_blank");
        }}
        onCreateBrief={(kw) => {
          window.open(`/content-brief?keyword=${encodeURIComponent(kw)}`, "_blank");
        }}
        onCreateLlmoBrief={(kw, competitorDomain) => {
          const params = new URLSearchParams({ keyword: kw });
          if (competitorDomain) params.set("competitor", competitorDomain);
          window.open(`/llmo-content-brief?${params.toString()}`, "_blank");
        }}
      />

      {/* Content Analysis — gaps + head-to-head */}
      <ContentGaps />

      {/* Recent Activity — new, updated, removed pages across all competitors */}
      <RecentActivity />

      {toast && <div className="fixed top-4 right-4 z-50 px-4 py-2 rounded-lg bg-slate-900 text-white text-xs font-semibold shadow-lg">{toast}</div>}
    </div>
  );
}
