/**
 * Pipeline admin dashboard — video list with status, retrigger/revoke,
 * expandable event log with token usage and collapsible JSON viewer.
 */

import { useCallback, useEffect, useRef, useState } from "react";
import { useSearchParams } from "react-router-dom";
import {
  fetchPipelineVideos,
  fetchPipelineEvents,
  fetchWorkerStatus,
  fetchDebugMode,
  setDebugMode,
  triggerPipeline,
  revokePipeline,
  type PipelineVideoItem,
  type PipelineEvent,
  type WorkerStatusResponse,
} from "../api/public-client";

// ── Helpers ──────────────────────────────────────────────────────────────────

function formatDate(iso: string | null): string {
  if (!iso) return "—";
  return new Date(iso).toLocaleString(undefined, {
    month: "short",
    day: "numeric",
    hour: "2-digit",
    minute: "2-digit",
    second: "2-digit",
  });
}

function formatTokens(n: number): string {
  if (n === 0) return "0";
  if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
  if (n >= 1_000) return `${(n / 1_000).toFixed(1)}k`;
  return String(n);
}

function statusBadgeClass(status: string): string {
  switch (status) {
    case "completed":
    case "indexed":
      return "pipeline-badge--success";
    case "processing":
    case "extracted":
    case "classified":
    case "synthesized":
      return "pipeline-badge--active";
    case "failed":
    case "error":
      return "pipeline-badge--error";
    case "pending":
    case "queued":
      return "pipeline-badge--pending";
    default:
      return "";
  }
}

function eventTypeIcon(eventType: string): string {
  switch (eventType) {
    case "start":
      return "▶";
    case "complete":
      return "✓";
    case "error":
      return "✗";
    case "llm_call":
      return "🤖";
    default:
      return "·";
  }
}

// ── Collapsible JSON ─────────────────────────────────────────────────────────

function JsonViewer({ data }: { data: Record<string, unknown> | null }) {
  const [open, setOpen] = useState(false);
  if (!data || Object.keys(data).length === 0) return null;

  return (
    <div className="json-viewer">
      <button
        className="json-viewer__toggle"
        onClick={() => setOpen((v) => !v)}
        aria-expanded={open}
      >
        {open ? "▾ Hide payload" : "▸ Show payload"}
      </button>
      {open && (
        <pre className="json-viewer__content">
          {JSON.stringify(data, null, 2)}
        </pre>
      )}
    </div>
  );
}

// ── Debug Payload Viewer ─────────────────────────────────────────────────────

interface DebugSection {
  label: string;
  content: string;
}

function DebugPayloadViewer({ event }: { event: PipelineEvent }) {
  const sections: DebugSection[] = [];
  if (event.system_prompt_text) sections.push({ label: "System Prompt", content: event.system_prompt_text });
  if (event.user_prompt_text) sections.push({ label: "User Prompt", content: event.user_prompt_text });
  if (event.response_text) sections.push({ label: "Response", content: event.response_text });

  const [openSections, setOpenSections] = useState<Record<string, boolean>>({});
  const [copiedKey, setCopiedKey] = useState<string | null>(null);

  if (sections.length === 0) return null;

  const toggleSection = (label: string) => {
    setOpenSections((prev) => ({ ...prev, [label]: !prev[label] }));
  };

  const copyToClipboard = async (label: string, text: string) => {
    try {
      await navigator.clipboard.writeText(text);
      setCopiedKey(label);
      setTimeout(() => setCopiedKey(null), 1500);
    } catch {
      // Fallback for non-HTTPS contexts
      const ta = document.createElement("textarea");
      ta.value = text;
      ta.style.position = "fixed";
      ta.style.opacity = "0";
      document.body.appendChild(ta);
      ta.select();
      document.execCommand("copy");
      document.body.removeChild(ta);
      setCopiedKey(label);
      setTimeout(() => setCopiedKey(null), 1500);
    }
  };

  const exportAsJson = () => {
    const data: Record<string, string | null> = {
      event_id: event.id,
      stage: event.stage,
      event_type: event.event_type,
      model: event.model,
      system_prompt_text: event.system_prompt_text,
      user_prompt_text: event.user_prompt_text,
      response_text: event.response_text,
    };
    const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `debug-${event.stage}-${event.id.slice(0, 8)}.json`;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div className="debug-viewer">
      <div className="debug-viewer__header">
        <span className="debug-viewer__label">LLM Debug</span>
        <button className="debug-viewer__export" onClick={exportAsJson} title="Export debug payload as JSON">
          ↓ JSON
        </button>
      </div>
      {sections.map((sec) => {
        const isOpen = !!openSections[sec.label];
        return (
          <div key={sec.label} className="debug-viewer__section">
            <div className="debug-viewer__section-header">
              <button
                className="debug-viewer__section-toggle"
                onClick={() => toggleSection(sec.label)}
                aria-expanded={isOpen}
              >
                {isOpen ? "▾" : "▸"} {sec.label}
              </button>
              <button
                className="debug-viewer__copy"
                onClick={() => void copyToClipboard(sec.label, sec.content)}
                title={`Copy ${sec.label}`}
              >
                {copiedKey === sec.label ? "✓ Copied" : "Copy"}
              </button>
            </div>
            {isOpen && (
              <pre className="debug-viewer__content">{sec.content}</pre>
            )}
          </div>
        );
      })}
    </div>
  );
}

// ── Event Log ────────────────────────────────────────────────────────────────

function EventLog({ videoId }: { videoId: string }) {
  const [events, setEvents] = useState<PipelineEvent[]>([]);
  const [total, setTotal] = useState(0);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [offset, setOffset] = useState(0);
  const [viewMode, setViewMode] = useState<"head" | "tail">("tail");
  const limit = 50;

  const load = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const res = await fetchPipelineEvents(videoId, {
        offset,
        limit,
        order: viewMode === "head" ? "asc" : "desc",
      });
      setEvents(res.items);
      setTotal(res.total);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to load events");
    } finally {
      setLoading(false);
    }
  }, [videoId, offset, viewMode]);

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

  if (loading) return <div className="loading">Loading events…</div>;
  if (error) return <div className="loading error-text">Error: {error}</div>;
  if (events.length === 0) return <div className="pipeline-events__empty">No events recorded.</div>;

  const hasNext = offset + limit < total;
  const hasPrev = offset > 0;

  return (
    <div className="pipeline-events">
      <div className="pipeline-events__header">
        <span className="pipeline-events__count">{total} event{total !== 1 ? "s" : ""}</span>
        <div className="pipeline-events__view-toggle">
          <button
            className={`pipeline-events__view-btn${viewMode === "head" ? " pipeline-events__view-btn--active" : ""}`}
            onClick={() => { setViewMode("head"); setOffset(0); }}
          >
            Oldest first
          </button>
          <button
            className={`pipeline-events__view-btn${viewMode === "tail" ? " pipeline-events__view-btn--active" : ""}`}
            onClick={() => { setViewMode("tail"); setOffset(0); }}
          >
            Newest first
          </button>
        </div>
      </div>

      <div className="pipeline-events__list">
        {events.map((evt) => (
          <div key={evt.id} className={`pipeline-event pipeline-event--${evt.event_type}`}>
            <div className="pipeline-event__row">
              <span className="pipeline-event__icon">{eventTypeIcon(evt.event_type)}</span>
              <span className="pipeline-event__stage">{evt.stage}</span>
              <span className={`pipeline-badge pipeline-badge--event-${evt.event_type}`}>
                {evt.event_type}
              </span>
              {evt.model && <span className="pipeline-event__model">{evt.model}</span>}
              {evt.total_tokens != null && evt.total_tokens > 0 && (
                <span className="pipeline-event__tokens" title={`prompt: ${evt.prompt_tokens ?? 0} / completion: ${evt.completion_tokens ?? 0}`}>
                  {formatTokens(evt.total_tokens)} tok
                </span>
              )}
              {evt.duration_ms != null && (
                <span className="pipeline-event__duration">{evt.duration_ms}ms</span>
              )}
              <span className="pipeline-event__time">{formatDate(evt.created_at)}</span>
            </div>
            <JsonViewer data={evt.payload} />
            <DebugPayloadViewer event={evt} />
          </div>
        ))}
      </div>

      {(hasPrev || hasNext) && (
        <div className="pipeline-events__pager">
          <button
            className="btn btn--small btn--secondary"
            disabled={!hasPrev}
            onClick={() => setOffset((o) => Math.max(0, o - limit))}
          >
            ← Prev
          </button>
          <span className="pipeline-events__pager-info">
            {offset + 1}–{Math.min(offset + limit, total)} of {total}
          </span>
          <button
            className="btn btn--small btn--secondary"
            disabled={!hasNext}
            onClick={() => setOffset((o) => o + limit)}
          >
            Next →
          </button>
        </div>
      )}
    </div>
  );
}

// ── Worker Status ────────────────────────────────────────────────────────────

function WorkerStatus() {
  const [status, setStatus] = useState<WorkerStatusResponse | null>(null);
  const [error, setError] = useState<string | null>(null);

  const load = useCallback(async () => {
    try {
      setError(null);
      const res = await fetchWorkerStatus();
      setStatus(res);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed");
    }
  }, []);

  useEffect(() => {
    void load();
    const id = setInterval(() => void load(), 15_000);
    return () => clearInterval(id);
  }, [load]);

  if (error) {
    return (
      <div className="worker-status worker-status--error">
        <span className="worker-status__dot worker-status__dot--offline" />
        Worker: error ({error})
      </div>
    );
  }

  if (!status) {
    return (
      <div className="worker-status">
        <span className="worker-status__dot worker-status__dot--unknown" />
        Worker: checking…
      </div>
    );
  }

  return (
    <div className={`worker-status ${status.online ? "worker-status--online" : "worker-status--offline"}`}>
      <span className={`worker-status__dot ${status.online ? "worker-status__dot--online" : "worker-status__dot--offline"}`} />
      <span className="worker-status__label">
        {status.online ? `${status.workers.length} worker${status.workers.length !== 1 ? "s" : ""} online` : "Workers offline"}
      </span>
      {status.workers.map((w) => (
        <span key={w.name} className="worker-status__detail" title={w.name}>
          {w.active_tasks.length > 0
            ? `${w.active_tasks.length} active`
            : "idle"}
          {w.pool_size != null && ` · pool ${w.pool_size}`}
        </span>
      ))}
    </div>
  );
}

// ── Debug Mode Toggle ────────────────────────────────────────────────────────

function DebugModeToggle({
  debugMode,
  onDebugModeChange,
}: {
  debugMode: boolean | null;
  onDebugModeChange: (mode: boolean) => void;
}) {
  const [debugLoading, setDebugLoading] = useState(false);

  async function handleToggle() {
    if (debugMode === null || debugLoading) return;
    setDebugLoading(true);
    try {
      const res = await setDebugMode(!debugMode);
      onDebugModeChange(res.debug_mode);
    } catch {
      // swallow — leave previous state
    } finally {
      setDebugLoading(false);
    }
  }

  if (debugMode === null) return null;

  return (
    <div className="debug-toggle" title="When enabled, pipeline runs capture full LLM prompts and responses for inspection in the event log">
      <span className="debug-toggle__label">
        Debug {debugMode ? "On" : "Off"}
      </span>
      <button
        type="button"
        className={`debug-toggle__switch ${debugMode ? "debug-toggle__switch--active" : ""}`}
        onClick={handleToggle}
        disabled={debugLoading}
        aria-label={`Turn debug mode ${debugMode ? "off" : "on"}`}
      />
    </div>
  );
}

// ── Status Filter ────────────────────────────────────────────────────────────

function StatusFilter({
  videos,
  activeFilter,
  onFilterChange,
}: {
  videos: PipelineVideoItem[];
  activeFilter: string | null;
  onFilterChange: (filter: string | null) => void;
}) {
  const statuses = Array.from(new Set(videos.map((v) => v.processing_status))).sort();

  if (statuses.length <= 1) return null;

  return (
    <div className="filter-tabs">
      <button
        type="button"
        className={`filter-tab ${activeFilter === null ? "filter-tab--active" : ""}`}
        onClick={() => onFilterChange(null)}
      >
        All
      </button>
      {statuses.map((status) => (
        <button
          key={status}
          type="button"
          className={`filter-tab ${activeFilter === status ? "filter-tab--active" : ""}`}
          onClick={() => onFilterChange(status)}
        >
          {status} ({videos.filter((v) => v.processing_status === status).length})
        </button>
      ))}
    </div>
  );
}

// ── Main Page ────────────────────────────────────────────────────────────────

export default function AdminPipeline() {
  const [searchParams] = useSearchParams();
  const [videos, setVideos] = useState<PipelineVideoItem[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [expandedId, setExpandedId] = useState<string | null>(null);
  const [actionLoading, setActionLoading] = useState<string | null>(null);
  const [actionMessage, setActionMessage] = useState<{ id: string; text: string; ok: boolean } | null>(null);
  const [activeFilter, setActiveFilter] = useState<string | null>(null);
  const [debugMode, setDebugModeState] = useState<boolean | null>(null);
  const videoRefs = useRef<Map<string, HTMLDivElement>>(new Map());
  const deepLinked = useRef(false);

  const load = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const res = await fetchPipelineVideos();
      setVideos(res.items);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to load videos");
    } finally {
      setLoading(false);
    }
  }, []);

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

  // Deep-link: auto-expand and scroll to ?video=<id> on first load
  useEffect(() => {
    if (deepLinked.current || loading || videos.length === 0) return;
    const targetVideoId = searchParams.get("video");
    if (!targetVideoId) return;
    const match = videos.find((v) => v.id === targetVideoId);
    if (!match) return;
    deepLinked.current = true;
    setExpandedId(targetVideoId);
    // Scroll after the expanded detail renders
    requestAnimationFrame(() => {
      const el = videoRefs.current.get(targetVideoId);
      if (el) el.scrollIntoView({ behavior: "smooth", block: "start" });
    });
  }, [loading, videos, searchParams]);

  useEffect(() => {
    let cancelled = false;
    fetchDebugMode()
      .then((res) => {
        if (!cancelled) setDebugModeState(res.debug_mode);
      })
      .catch(() => {
        // silently fail — toggle stays hidden
      });
    return () => { cancelled = true; };
  }, []);

  const handleTrigger = async (videoId: string) => {
    setActionLoading(videoId);
    setActionMessage(null);
    try {
      const res = await triggerPipeline(videoId);
      setActionMessage({ id: videoId, text: `Triggered (${res.status})`, ok: true });
      // Refresh after short delay to let status update
      setTimeout(() => void load(), 2000);
    } catch (err) {
      setActionMessage({
        id: videoId,
        text: err instanceof Error ? err.message : "Trigger failed",
        ok: false,
      });
    } finally {
      setActionLoading(null);
    }
  };

  const handleRevoke = async (videoId: string) => {
    setActionLoading(videoId);
    setActionMessage(null);
    try {
      const res = await revokePipeline(videoId);
      setActionMessage({
        id: videoId,
        text: res.tasks_revoked > 0
          ? `Revoked ${res.tasks_revoked} task${res.tasks_revoked !== 1 ? "s" : ""}`
          : "No active tasks",
        ok: true,
      });
      setTimeout(() => void load(), 2000);
    } catch (err) {
      setActionMessage({
        id: videoId,
        text: err instanceof Error ? err.message : "Revoke failed",
        ok: false,
      });
    } finally {
      setActionLoading(null);
    }
  };

  const toggleExpand = (id: string) => {
    setExpandedId((prev) => (prev === id ? null : id));
  };

  return (
    <div className="admin-pipeline">
      <div className="admin-pipeline__header">
        <div>
          <h2 className="admin-pipeline__title">Pipeline Management</h2>
          <p className="admin-pipeline__subtitle">
            {videos.length} video{videos.length !== 1 ? "s" : ""}
          </p>
        </div>
        <div className="admin-pipeline__header-right">
          <DebugModeToggle debugMode={debugMode} onDebugModeChange={setDebugModeState} />
          <WorkerStatus />
          <button className="btn btn--secondary" onClick={() => void load()} disabled={loading}>
            ↻ Refresh
          </button>
        </div>
      </div>

      {loading ? (
        <div className="loading">Loading videos…</div>
      ) : error ? (
        <div className="loading error-text">Error: {error}</div>
      ) : videos.length === 0 ? (
        <div className="empty-state">No videos in pipeline.</div>
      ) : (
        <>
          <StatusFilter
            videos={videos}
            activeFilter={activeFilter}
            onFilterChange={setActiveFilter}
          />
          <div className="admin-pipeline__list">
            {videos
              .filter((v) => activeFilter === null || v.processing_status === activeFilter)
              .map((video) => (
            <div key={video.id} className="pipeline-video" ref={(el) => { if (el) videoRefs.current.set(video.id, el); }}>
              <div
                className="pipeline-video__header"
                onClick={() => toggleExpand(video.id)}
              >
                <div className="pipeline-video__info">
                  <span className="pipeline-video__filename" title={video.filename}>
                    {video.filename}
                  </span>
                  <span className="pipeline-video__creator">{video.creator_name}</span>
                </div>

                <div className="pipeline-video__meta">
                  <span className={`pipeline-badge ${statusBadgeClass(video.processing_status)}`}>
                    {video.processing_status}
                  </span>
                  <span className="pipeline-video__stat" title="Events">
                    {video.event_count} events
                  </span>
                  <span className="pipeline-video__stat" title="Total tokens used">
                    {formatTokens(video.total_tokens_used)} tokens
                  </span>
                  <span className="pipeline-video__time">
                    {formatDate(video.last_event_at)}
                  </span>
                  <a
                    className="pipeline-video__review-link"
                    href="/admin/review"
                    title={`Review moments from ${video.creator_name}`}
                    onClick={(e) => e.stopPropagation()}
                  >
                    → Moments
                  </a>
                </div>

                <div className="pipeline-video__actions" onClick={(e) => e.stopPropagation()}>
                  <button
                    className="btn btn--small btn--primary"
                    onClick={() => void handleTrigger(video.id)}
                    disabled={actionLoading === video.id}
                    title="Retrigger pipeline"
                  >
                    {actionLoading === video.id ? "…" : debugMode ? "▶ Trigger (debug)" : "▶ Trigger"}
                  </button>
                  <button
                    className="btn btn--small btn--danger"
                    onClick={() => void handleRevoke(video.id)}
                    disabled={actionLoading === video.id}
                    title="Revoke active tasks"
                  >
                    {actionLoading === video.id ? "…" : "■ Revoke"}
                  </button>
                </div>
              </div>

              {actionMessage?.id === video.id && (
                <div className={`pipeline-video__message ${actionMessage.ok ? "pipeline-video__message--ok" : "pipeline-video__message--err"}`}>
                  {actionMessage.text}
                </div>
              )}

              {expandedId === video.id && (
                <div className="pipeline-video__detail">
                  <div className="pipeline-video__detail-meta">
                    <span>Created: {formatDate(video.created_at)}</span>
                    <span>Updated: {formatDate(video.updated_at)}</span>
                  </div>
                  <EventLog videoId={video.id} />
                </div>
              )}
            </div>
          ))}
        </div>
        </>
      )}
    </div>
  );
}
