"use client";

import { ExternalLink, Pause, Play, RefreshCw } from "lucide-react";
import { useCallback, useEffect, useRef, useState } from "react";

import {
  extractDouyinCurrentRoomFromLog,
  extractDouyinLiveUrls,
  extractDouyinRoomHints,
} from "@/lib/youtubeConfigUtils";

type TFn = (zh: string, en: string) => string;

type StatusPayload = {
  process_status?: string;
  recent_log?: string;
  recent_error?: string;
  business_status?: string;
  business_note?: string;
  ffmpeg_pids?: number[];
  log_age_seconds?: number | null;
  is_pushing?: boolean;
};

function fmtDuration(sec: number): string {
  if (sec < 60) return `${sec}s`;
  const m = Math.floor(sec / 60);
  const s = sec % 60;
  if (m < 60) return `${m}m ${s}s`;
  const h = Math.floor(m / 60);
  const mm = m % 60;
  return `${h}h ${mm}m`;
}

function statusLine(t: TFn, raw: string | undefined): string {
  const s = (raw || "unknown").toLowerCase();
  if (s === "online" || s === "running") return t("运行中（PM2/进程在线）", "Running (process online)");
  if (s === "stopped" || s === "stopping") return t("已停止", "Stopped");
  if (s === "not_found" || s === "errored") return t("未运行或未找到", "Not running / not found");
  if (s === "invalid") return t("无效进程名", "Invalid process");
  return raw || "—";
}

type Props = {
  t: TFn;
  currentProc: string;
  fetchJson: <T,>(path: string, init?: RequestInit) => Promise<T>;
  /** 用于源站房间线索（与 Electron 一致） */
  urlListText: string;
  /** YouTube 密钥尾码；非 YouTube 进程可传空并关 showKeyRow */
  keySuffixUi: string;
  showKeyRow?: boolean;
  pollMs?: number;
  refreshToken?: number;
};

function businessLine(t: TFn, raw: string | undefined, note: string | undefined): string {
  const s = (raw || "").toLowerCase();
  if (s === "streaming") return t("Pushing to YouTube", "Pushing to YouTube");
  if (s === "waiting_source") return t("Waiting for the upstream live source", "Waiting for the upstream live source");
  if (s === "source_live")
    return t("Source live detected, but no active FFmpeg relay worker", "Source live detected, but no active FFmpeg relay worker");
  if (s === "monitoring") return t("Process online and monitoring the source", "Process online and monitoring the source");
  if (s === "misconfigured") return t("YouTube key missing or invalid", "YouTube key missing or invalid");
  if (s === "stale") return t("Process online but the log heartbeat is stale", "Process online but the log heartbeat is stale");
  if (s === "error") return t("Recent process error detected", "Recent process error detected");
  if (note?.trim()) return note.trim();
  return t("No business-state hint yet", "No business-state hint yet");
}

export function LiveProcessLiveLogCard({
  t,
  currentProc,
  fetchJson,
  urlListText,
  keySuffixUi,
  showKeyRow = true,
  pollMs = 1000,
  refreshToken = 0,
}: Props) {
  const [processStatus, setProcessStatus] = useState<string>("");
  const [recentLog, setRecentLog] = useState("");
  const [recentError, setRecentError] = useState("");
  const [businessStatus, setBusinessStatus] = useState("");
  const [businessNote, setBusinessNote] = useState("");
  const [ffmpegPids, setFfmpegPids] = useState<number[]>([]);
  const [logAgeSeconds, setLogAgeSeconds] = useState<number | null>(null);
  const [paused, setPaused] = useState(false);
  const [liveSince, setLiveSince] = useState<number | null>(null);
  const [nowTick, setNowTick] = useState(0);
  const outRef = useRef<HTMLPreElement>(null);
  const errRef = useRef<HTMLPreElement>(null);
  const prevOnlineRef = useRef(false);
  const pullInFlightRef = useRef(false);
  const pullQueuedRef = useRef(false);
  const currentProcRef = useRef(currentProc);
  const requestSeqRef = useRef(0);

  const douyinHints = extractDouyinRoomHints(urlListText);
  const onlineNow = /^(online|running)$/i.test(processStatus);
  const roomFromLog = extractDouyinCurrentRoomFromLog(recentLog);
  const fallbackUrl = extractDouyinLiveUrls(urlListText)[0] ?? "";
  const primaryRoomId = roomFromLog || (fallbackUrl.match(/live\.douyin\.com\/([a-zA-Z0-9_-]+)/i)?.[1] ?? null);
  const primaryUrl = primaryRoomId ? `https://live.douyin.com/${primaryRoomId}` : fallbackUrl || null;

  const pull = useCallback(async () => {
    const targetProc = currentProcRef.current.trim();
    if (!targetProc) return;
    if (pullInFlightRef.current) {
      pullQueuedRef.current = true;
      return;
    }
    pullInFlightRef.current = true;
    const requestId = ++requestSeqRef.current;
    try {
      const data = await fetchJson<StatusPayload>(
        `/status?process=${encodeURIComponent(targetProc)}`,
      );
      if (requestId !== requestSeqRef.current || targetProc !== currentProcRef.current.trim()) return;
      const ps = data.process_status || "unknown";
      setProcessStatus(ps);
      setRecentLog(data.recent_log || "");
      setRecentError(data.recent_error || "");
      setBusinessStatus(data.business_status || "");
      setBusinessNote(data.business_note || "");
      setFfmpegPids(Array.isArray(data.ffmpeg_pids) ? data.ffmpeg_pids : []);
      setLogAgeSeconds(typeof data.log_age_seconds === "number" ? data.log_age_seconds : null);
      const online = /^(online|running)$/i.test(ps);
      if (online && !prevOnlineRef.current) {
        setLiveSince(Date.now());
      }
      if (!online) {
        setLiveSince(null);
      }
      prevOnlineRef.current = online;
    } catch {
      if (requestId !== requestSeqRef.current || targetProc !== currentProcRef.current.trim()) return;
      setProcessStatus("error");
      setBusinessStatus("error");
      setBusinessNote("");
      setFfmpegPids([]);
      setLogAgeSeconds(null);
    } finally {
      pullInFlightRef.current = false;
      if (pullQueuedRef.current) {
        pullQueuedRef.current = false;
        window.setTimeout(() => void pull(), 0);
      }
    }
  }, [fetchJson]);

  useEffect(() => {
    currentProcRef.current = currentProc;
    requestSeqRef.current += 1;
    pullQueuedRef.current = false;
    if (!currentProc) {
      setProcessStatus("");
      setRecentLog("");
      setRecentError("");
      setBusinessStatus("");
      setBusinessNote("");
      setFfmpegPids([]);
      setLogAgeSeconds(null);
      setLiveSince(null);
      prevOnlineRef.current = false;
      return;
    }
    setProcessStatus("");
    setRecentLog("");
    setRecentError("");
    setBusinessStatus("");
    setBusinessNote("");
    setFfmpegPids([]);
    setLogAgeSeconds(null);
    setLiveSince(null);
    prevOnlineRef.current = false;
    void pull();
  }, [currentProc, pull]);

  useEffect(() => {
    if (!currentProc) return;
    void pull();
  }, [currentProc, pull, refreshToken]);

  useEffect(() => {
    if (paused || !currentProc) return;
    const id = window.setInterval(() => void pull(), pollMs);
    return () => clearInterval(id);
  }, [paused, currentProc, pull, pollMs]);

  useEffect(() => {
    const onVis = () => {
      if (document.visibilityState === "visible") void pull();
    };
    document.addEventListener("visibilitychange", onVis);
    return () => document.removeEventListener("visibilitychange", onVis);
  }, [pull]);

  useEffect(() => {
    if (!liveSince) return;
    const id = window.setInterval(() => setNowTick((n) => n + 1), 1000);
    return () => clearInterval(id);
  }, [liveSince]);

  useEffect(() => {
    if (paused) return;
    const el = outRef.current;
    if (el) el.scrollTop = el.scrollHeight;
  }, [recentLog, paused]);

  useEffect(() => {
    if (paused) return;
    const el = errRef.current;
    if (el) el.scrollTop = el.scrollHeight;
  }, [recentError, paused]);

  const durationLabel =
    liveSince == null
      ? t("—（未检测到在线）", "— (not online)")
      : fmtDuration(Math.max(0, Math.floor((Date.now() - liveSince) / 1000)));

  return (
    <section className="rounded-2xl border border-emerald-500/20 bg-gradient-to-b from-emerald-500/[0.07] to-zinc-950/80 p-6 ring-1 ring-white/[0.04]">
      <span className="sr-only" aria-hidden>
        {nowTick}
      </span>
      <div className="flex flex-wrap items-center justify-between gap-3">
        <div className="flex items-center gap-2">
          <h3 className="text-sm font-semibold text-white">{t("当前直播日志", "Live process log")}</h3>
          <span className="rounded-full bg-white/10 px-2 py-0.5 font-mono text-[10px] text-zinc-300 ring-1 ring-white/10">
            {currentProc || "—"}
          </span>
          <span className="rounded-full bg-emerald-500/15 px-2 py-0.5 text-[10px] font-medium text-emerald-200/90 ring-1 ring-emerald-500/25">
            {paused ? t("已暂停刷新", "Paused") : t("每秒刷新", "~1s refresh")}
          </span>
        </div>
        <div className="flex flex-wrap gap-2">
          <button
            type="button"
            onClick={() => void pull()}
            className="inline-flex items-center gap-1 rounded-lg border border-white/10 bg-white/[0.05] px-3 py-1.5 text-xs text-zinc-300 hover:bg-white/[0.08]"
          >
            <RefreshCw className="h-3.5 w-3.5" />
            {t("立即拉取", "Refresh")}
          </button>
          <button
            type="button"
            onClick={() => setPaused((p) => !p)}
            className={`inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-xs ring-1 ${
              paused
                ? "bg-amber-500/20 text-amber-100 ring-amber-500/35"
                : "border border-white/10 bg-black/30 text-zinc-300"
            }`}
          >
            {paused ? <Play className="h-3.5 w-3.5" /> : <Pause className="h-3.5 w-3.5" />}
            {paused ? t("继续自动刷新", "Resume") : t("暂停自动刷新", "Pause")}
          </button>
        </div>
      </div>

      <div className="mt-4 space-y-3 rounded-xl border border-white/[0.06] bg-black/25 p-4">
        <div className="grid gap-2 text-xs sm:grid-cols-2">
          <div className="flex flex-wrap gap-2">
            <span className="text-zinc-500">{t("进程状态", "Status")}</span>
            <span className="font-medium text-zinc-200">{statusLine(t, processStatus)}</span>
          </div>
          <div className="flex flex-wrap gap-2">
            <span className="text-zinc-500">{t("Business state", "Business state")}</span>
            <span className="font-medium text-cyan-200/90">{businessLine(t, businessStatus, businessNote)}</span>
          </div>
          <div className="flex flex-wrap gap-2">
            <span className="text-zinc-500">{t("本次在线时长（估算）", "Online duration (est.)")}</span>
            <span className="font-mono text-cyan-200/90">{durationLabel}</span>
          </div>
          <div className="flex flex-wrap gap-2">
            <span className="text-zinc-500">{t("FFmpeg workers", "FFmpeg workers")}</span>
            <span className="font-mono text-zinc-300">
              {ffmpegPids.length ? ffmpegPids.join(", ") : "none"}
              {logAgeSeconds != null ? ` · log ${fmtDuration(Math.max(0, Math.floor(logAgeSeconds)))}` : ""}
            </span>
          </div>
        </div>
        {showKeyRow ? (
          <div className="flex flex-wrap gap-2 text-xs">
            <span className="text-zinc-500">{t("YouTube key 尾码（参考）", "YT key suffix")}</span>
            <span className="font-mono text-zinc-300">{keySuffixUi || "—"}</span>
          </div>
        ) : null}
        {primaryUrl ? (
          <div className="rounded-lg border border-violet-500/20 bg-violet-500/[0.06] px-3 py-2.5">
            <div className="flex flex-wrap items-center justify-between gap-2">
              <span className="text-[11px] font-medium text-zinc-400">
                {roomFromLog
                  ? t("当前直播源站（日志）", "Current source (from log)")
                  : onlineNow
                    ? t("配置中的源站地址", "Configured source URL")
                    : t("源站快捷入口", "Source quick link")}
              </span>
              <a
                href={primaryUrl}
                target="_blank"
                rel="noreferrer"
                className="inline-flex items-center gap-1.5 rounded-lg bg-violet-500/20 px-3 py-1.5 text-xs font-medium text-violet-100 ring-1 ring-violet-500/35 transition hover:bg-violet-500/30"
              >
                <ExternalLink className="h-3.5 w-3.5 shrink-0 opacity-90" />
                {primaryRoomId || t("打开抖音直播页", "Open Douyin live")}
              </a>
            </div>
            {douyinHints.length > 1 && roomFromLog ? (
              <p className="mt-2 text-[10px] text-zinc-600">
                {t("配置中还有", "Also in config:")}{" "}
                {douyinHints
                  .filter((id) => id !== primaryRoomId)
                  .slice(0, 3)
                  .map((id) => (
                    <a
                      key={id}
                      href={`https://live.douyin.com/${id}`}
                      target="_blank"
                      rel="noreferrer"
                      className="ml-1 text-violet-400/90 underline-offset-2 hover:underline"
                    >
                      {id}
                    </a>
                  ))}
                {douyinHints.length > 4 ? "…" : null}
              </p>
            ) : null}
          </div>
        ) : (
          <p className="text-[11px] text-zinc-600">
            {t("（日志与地址列表中暂无抖音房间）", "(No Douyin room in log or URL list)")}
          </p>
        )}
      </div>

      {processStatus !== "not_found" && processStatus !== "invalid" ? (
        <div className="mt-4 grid gap-4 lg:grid-cols-2">
          <div>
            <div className="text-[11px] font-semibold uppercase tracking-wider text-emerald-400/90">
              {t("进程输出（stdout / 合并日志）", "Process output")}
            </div>
            <pre
              ref={outRef}
              className="log-live-scroll mt-2 max-h-[min(22rem,42vh)] min-h-[12rem] overflow-auto whitespace-pre-wrap rounded-xl border border-emerald-500/20 bg-black/55 p-3 font-mono text-[10px] leading-relaxed text-emerald-100/95"
            >
              {recentLog.trim() || t("（暂无）", "(empty)")}
            </pre>
          </div>
          <div>
            <div className="text-[11px] font-semibold uppercase tracking-wider text-rose-400/90">
              {t("异常信息（stderr）", "Stderr")}
            </div>
            <pre
              ref={errRef}
              className="log-live-scroll mt-2 max-h-[min(22rem,42vh)] min-h-[12rem] overflow-auto whitespace-pre-wrap rounded-xl border border-rose-500/20 bg-black/55 p-3 font-mono text-[10px] leading-relaxed text-rose-100/95"
            >
              {recentError.trim() || t("（暂无）", "(empty)")}
            </pre>
          </div>
        </div>
      ) : (
        <p className="mt-4 text-xs text-zinc-500">
          {t("进程未在 PM2 / 本地注册表中运行，日志区域已隐藏。", "Process not running — log panes hidden.")}
        </p>
      )}
    </section>
  );
}
