import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import i18n from "../i18n";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import {
  Activity,
  AlertTriangle,
  Check,
  Clock,
  Database,
  Gauge,
  Pause,
  Pencil,
  Play,
  RadioTower,
  Trash2,
  X,
} from "lucide-react";
import { api, type SchedulerJob, type SchedulerStatus } from "../lib/api";
import { useLiveQuery } from "../lib/useLiveQuery";
import { useConfirm } from "./ConfirmProvider";
import { relativeTime } from "../lib/format";
import { notify } from "../lib/notifications";
import Tooltip from "./Tooltip";

const POLL_MS = 4000;
// While any job is running, poll faster so short scheduled runs aren't missed between ticks.
const FAST_POLL_MS = 1500;

// Seconds until an ISO instant (negative = past).
function secsUntil(iso: string | null): number | null {
  if (!iso) return null;
  return Math.round((new Date(iso).getTime() - Date.now()) / 1000);
}

function fmtCountdown(s: number): string {
  if (s <= 0) return i18n.t("time.eta.now");
  if (s < 60) return `${s}s`;
  const m = Math.floor(s / 60);
  if (m < 60) return `${m}m ${s % 60}s`;
  const h = Math.floor(m / 60);
  return `${h}h ${m % 60}m`;
}

const DOT_CLASS: Record<string, string> = {
  running: "bg-accent animate-pulse",
  ok: "bg-emerald-500",
  error: "bg-red-500",
  skipped: "bg-amber-500",
  idle: "bg-border",
};

function statusKey(job: SchedulerJob): keyof typeof DOT_CLASS {
  if (job.running) return "running";
  if (job.status === "ok") return "ok";
  if (job.status === "error") return "error";
  if (job.status === "skipped") return "skipped";
  return "idle";
}

function StatusDot({ k, withTooltip = true }: { k: keyof typeof DOT_CLASS; withTooltip?: boolean }) {
  const { t } = useTranslation();
  const dot = <span className={`inline-block w-2.5 h-2.5 rounded-full shrink-0 ${DOT_CLASS[k]}`} />;
  return withTooltip ? <Tooltip hint={t(`scheduler.dot.${k}`)}>{dot}</Tooltip> : dot;
}

function StatusLegend() {
  const { t } = useTranslation();
  return (
    <div className="flex items-center gap-x-3 gap-y-1 flex-wrap text-[11px] text-muted mb-2">
      {(["running", "ok", "idle", "error", "skipped"] as const).map((k) => (
        <span key={k} className="inline-flex items-center gap-1.5">
          <StatusDot k={k} withTooltip={false} />
          {t(`scheduler.dot.${k}`)}
        </span>
      ))}
    </div>
  );
}

// Shown for ANY running job. With reported counts it's a determinate bar; for a job that
// runs but doesn't report progress (or hasn't yet) it falls back to a generic "working"
// label and an indeterminate sliver — so every active run is visible, not just the ones
// that report numbers.
function JobProgress({ p }: { p: SchedulerJob["progress"] }) {
  const { t } = useTranslation();
  const phase = p?.phase ? t(`scheduler.phase.${p.phase}`, p.phase) : t("scheduler.phase.working");
  const pct =
    p?.total && p.total > 0 ? Math.min(100, Math.round((p.current / p.total) * 100)) : null;
  return (
    <div className="mt-1.5">
      <div className="flex items-center justify-between text-[11px] text-muted mb-0.5">
        <span className="truncate">{phase}</span>
        <span className="tabular-nums shrink-0">
          {p?.total != null
            ? `${p.current.toLocaleString()} / ${p.total.toLocaleString()}`
            : p
              ? p.current.toLocaleString()
              : ""}
        </span>
      </div>
      <div className="h-1.5 rounded-full bg-border overflow-hidden">
        {pct != null ? (
          <div className="h-full rounded-full bg-accent transition-[width]" style={{ width: `${pct}%` }} />
        ) : (
          // Indeterminate: total unknown, so a slim moving sliver instead of a fill.
          <div className="h-full w-1/3 rounded-full bg-accent animate-pulse" />
        )}
      </div>
    </div>
  );
}

function JobRow({
  job,
  onSave,
  saving,
  onRun,
  runDisabled,
}: {
  job: SchedulerJob;
  onSave: (minutes: number) => void;
  saving: boolean;
  onRun: () => void;
  runDisabled: boolean;
}) {
  const { t } = useTranslation();
  const [editing, setEditing] = useState(false);
  const [val, setVal] = useState(String(job.interval_minutes));
  const next = secsUntil(job.next_run);
  useEffect(() => {
    if (!editing) setVal(String(job.interval_minutes));
  }, [job.interval_minutes, editing]);

  function save() {
    const m = parseInt(val, 10);
    if (Number.isFinite(m) && m >= 1 && m <= 1440 && m !== job.interval_minutes) onSave(m);
    setEditing(false);
  }

  return (
    <div className="glass-card rounded-xl p-3 flex items-center gap-3">
      <StatusDot k={statusKey(job)} />
      <div className="min-w-0 flex-1">
        <div className="text-sm font-medium flex items-center gap-1.5 flex-wrap">
          <Tooltip hint={t(`scheduler.jobDesc.${job.id}`, "")}>
            <span className="underline decoration-dotted decoration-muted/40 underline-offset-4 cursor-help">
              {t(`scheduler.jobs.${job.id}`, job.id)}
            </span>
          </Tooltip>
          {editing ? (
            <span className="inline-flex items-center gap-1">
              <input
                type="number"
                min={1}
                max={1440}
                value={val}
                autoFocus
                onChange={(e) => setVal(e.target.value)}
                onKeyDown={(e) => {
                  if (e.key === "Enter") save();
                  if (e.key === "Escape") setEditing(false);
                }}
                className="w-16 bg-card border border-border rounded px-1.5 py-0.5 text-xs outline-none focus:border-accent"
              />
              <span className="text-muted text-xs">{t("scheduler.minutes")}</span>
              <button onClick={save} disabled={saving} className="text-emerald-400 hover:opacity-80 disabled:opacity-50" aria-label={t("common.save")}>
                <Check className="w-3.5 h-3.5" />
              </button>
              <button onClick={() => setEditing(false)} className="text-muted hover:text-fg" aria-label={t("common.cancel")}>
                <X className="w-3.5 h-3.5" />
              </button>
            </span>
          ) : (
            <Tooltip hint={t("scheduler.editInterval")}>
              <button
                onClick={() => setEditing(true)}
                className="group text-muted font-normal hover:text-accent inline-flex items-center gap-1 transition"
              >
                · {t("scheduler.everyMin", { count: job.interval_minutes })}
                <Pencil className="w-3 h-3 opacity-0 group-hover:opacity-100 transition" />
              </button>
            </Tooltip>
          )}
        </div>
        <div className="text-[11px] text-muted truncate">
          {job.running
            ? t("scheduler.runningNow")
            : job.last_finished
              ? t("scheduler.lastRun", { time: relativeTime(job.last_finished) })
              : t("scheduler.neverRun")}
          {job.last_error ? (
            <span className="text-red-400"> · {job.last_error}</span>
          ) : job.last_result ? (
            <span> · {job.last_result}</span>
          ) : null}
        </div>
        {job.running && <JobProgress p={job.progress} />}
      </div>
      <div className="shrink-0 text-right text-[11px] text-muted tabular-nums">
        {job.running ? (
          <span className="text-accent">{t("scheduler.active")}</span>
        ) : next != null ? (
          <>
            <div className="uppercase tracking-wide text-[10px]">{t("scheduler.nextRun")}</div>
            <div>{fmtCountdown(next)}</div>
          </>
        ) : (
          "—"
        )}
      </div>
      <Tooltip hint={t("scheduler.runNow")}>
        <button
          onClick={onRun}
          disabled={job.running || runDisabled}
          aria-label={t("scheduler.runNow")}
          className="shrink-0 p-1.5 rounded-lg text-muted hover:text-accent hover:bg-card transition disabled:opacity-30 disabled:hover:text-muted disabled:hover:bg-transparent"
        >
          <Play className="w-4 h-4" />
        </button>
      </Tooltip>
    </div>
  );
}

function MaintenanceSettings({
  m,
  onSave,
  saving,
}: {
  m: SchedulerStatus["maintenance"];
  onSave: (batch: number) => void;
  saving: boolean;
}) {
  const { t } = useTranslation();
  const [val, setVal] = useState(String(m.revalidate_batch));
  useEffect(() => setVal(String(m.revalidate_batch)), [m.revalidate_batch]);

  function save() {
    const n = parseInt(val, 10);
    if (Number.isFinite(n) && n >= m.min && n <= m.max && n !== m.revalidate_batch) onSave(n);
  }
  const dirty = String(m.revalidate_batch) !== val;

  return (
    <div className="glass-card rounded-xl p-4">
      <div className="flex items-center gap-2 text-sm flex-wrap">
        <Database className="w-4 h-4 text-muted shrink-0" />
        <Tooltip hint={t("scheduler.maintenance.batchHint")}>
          <span className="underline decoration-dotted decoration-muted/40 underline-offset-4 cursor-help">
            {t("scheduler.maintenance.batchLabel")}
          </span>
        </Tooltip>
        <span className="flex-1" />
        <input
          type="number"
          min={m.min}
          max={m.max}
          step={1000}
          value={val}
          onChange={(e) => setVal(e.target.value)}
          onKeyDown={(e) => e.key === "Enter" && save()}
          aria-label={t("scheduler.maintenance.batchLabel")}
          className="w-28 bg-card border border-border rounded px-2 py-1 text-sm tabular-nums outline-none focus:border-accent"
        />
        <button
          onClick={save}
          disabled={!dirty || saving}
          className="glass-card glass-hover px-3 py-1.5 rounded-lg text-sm disabled:opacity-40 transition"
        >
          {t("common.save")}
        </button>
      </div>
      <div className="text-[11px] text-muted mt-1.5">
        {t("scheduler.maintenance.batchNote", { default: m.revalidate_batch_default.toLocaleString() })}
      </div>
    </div>
  );
}

function Stat({
  icon: Icon,
  label,
  value,
  hint,
  sub,
}: {
  icon: typeof Database;
  label: string;
  value: string | number;
  hint?: string;
  sub?: string;
}) {
  return (
    <div className="glass-card rounded-xl p-3">
      <div className="flex items-center gap-2 text-muted text-xs">
        <Icon className="w-4 h-4 shrink-0" />
        <Tooltip hint={hint ?? ""}>
          <span className={hint ? "underline decoration-dotted decoration-muted/40 underline-offset-4 cursor-help" : ""}>
            {label}
          </span>
        </Tooltip>
      </div>
      <div className="text-xl font-semibold mt-1 tabular-nums">{value}</div>
      {sub && <div className="text-[11px] text-muted">{sub}</div>}
    </div>
  );
}

export default function Scheduler() {
  const { t } = useTranslation();
  const qc = useQueryClient();
  const confirm = useConfirm();
  // Poll faster while any job is running so live progress is smooth, then ease back when
  // idle. Derived from the freshest data by react-query itself (see useLiveQuery).
  const q = useLiveQuery<SchedulerStatus>(["scheduler"], api.schedulerStatus, {
    intervalMs: (d) => (d?.jobs?.some((j) => j.running) ? FAST_POLL_MS : POLL_MS),
  });
  const data = q.data;

  // Tick once a second so the per-job countdowns move between polls — but only while the tab is
  // visible; a hidden tab doesn't need to re-render the whole page every second (SB3).
  const [, setTick] = useState(0);
  useEffect(() => {
    let id: ReturnType<typeof setInterval> | undefined;
    const start = () => {
      if (id == null) id = setInterval(() => setTick((n) => n + 1), 1000);
    };
    const stop = () => {
      if (id != null) {
        clearInterval(id);
        id = undefined;
      }
    };
    const onVisibility = () => {
      if (document.hidden) stop();
      else {
        setTick((n) => n + 1); // snap the countdown to the correct value on return
        start();
      }
    };
    onVisibility(); // honor the current visibility on mount
    document.addEventListener("visibilitychange", onVisibility);
    return () => {
      stop();
      document.removeEventListener("visibilitychange", onVisibility);
    };
  }, []);

  const pauseResume = useMutation({
    mutationFn: (paused: boolean) => (paused ? api.resumeSync() : api.pauseSync()),
    onSuccess: () => qc.invalidateQueries({ queryKey: ["scheduler"] }),
    onError: () => notify({ level: "error", message: t("scheduler.toggleFailed") }),
  });

  const intervalMut = useMutation({
    mutationFn: (v: { jobId: string; minutes: number }) =>
      api.updateSchedulerJob(v.jobId, v.minutes),
    onSuccess: () => qc.invalidateQueries({ queryKey: ["scheduler"] }),
    onError: () => notify({ level: "error", message: t("scheduler.intervalFailed") }),
  });

  // Manual "run now" triggers. The wrapper runs in a background thread server-side; the
  // live poll surfaces the "running" state within a tick, so we just nudge a refetch.
  const runMut = useMutation({
    mutationFn: (jobId: string) => api.runSchedulerJob(jobId),
    onSuccess: (_d, jobId) => {
      notify({ level: "success", message: t("scheduler.triggered", { job: t(`scheduler.jobs.${jobId}`, jobId) }) });
      qc.invalidateQueries({ queryKey: ["scheduler"] });
    },
  });
  const runAllMut = useMutation({
    mutationFn: () => api.runAllSchedulerJobs(),
    onSuccess: (res) => {
      notify({ level: "success", message: t("scheduler.triggeredAll", { count: res.started.length }) });
      qc.invalidateQueries({ queryKey: ["scheduler"] });
    },
  });
  const batchMut = useMutation({
    mutationFn: (v: number) => api.updateMaintenanceBatch(v),
    onSuccess: () => qc.invalidateQueries({ queryKey: ["scheduler"] }),
  });
  const purgeMut = useMutation({
    mutationFn: () => api.purgeDiscovery(),
    onSuccess: (res) => {
      const removed =
        (res.search_videos_deleted ?? 0) + (res.videos_deleted ?? 0) + (res.channels_deleted ?? 0);
      notify({ level: "success", message: t("scheduler.purgedDiscovery", { count: removed }) });
      qc.invalidateQueries({ queryKey: ["scheduler"] });
    },
  });

  if (q.isLoading && !data)
    return <div className="p-8 text-muted">{t("scheduler.loading")}</div>;
  if (!data)
    return <div className="p-8 text-muted">{t("common.somethingWrong")}</div>;

  const quotaPct = data.quota.daily_budget
    ? Math.min(100, Math.round((data.quota.used_today / data.quota.daily_budget) * 100))
    : 0;

  return (
    <div className="p-4 max-w-4xl w-full mx-auto space-y-4">
      {/* Status strip */}
      <div className="glass rounded-2xl p-4 flex flex-wrap items-center gap-3">
        <Activity className="w-5 h-5 text-accent shrink-0" />
        <div className="min-w-0">
          <div className="font-semibold">{t("scheduler.title")}</div>
          <div className="text-xs text-muted flex items-center gap-2 flex-wrap">
            <span className="inline-flex items-center gap-1">
              <span
                className={`w-2 h-2 rounded-full ${
                  data.paused ? "bg-amber-500" : data.running_here ? "bg-emerald-500" : "bg-border"
                }`}
              />
              {data.paused
                ? t("scheduler.paused")
                : data.running_here
                  ? t("scheduler.running")
                  : t("scheduler.notHere")}
            </span>
            {q.isFetching && <span className="text-accent">· {t("scheduler.updating")}</span>}
          </div>
        </div>
        <div className="flex-1" />
        <Tooltip hint={data.paused ? t("scheduler.runAllPausedHint") : t("scheduler.runAllHint")}>
          <button
            onClick={() => runAllMut.mutate()}
            disabled={runAllMut.isPending || data.paused}
            className="glass-card glass-hover flex items-center gap-2 px-3 py-2 rounded-xl text-sm disabled:opacity-50 transition"
          >
            <Play className="w-4 h-4" />
            {t("scheduler.runAll")}
          </button>
        </Tooltip>
        <Tooltip hint={t("scheduler.purgeDiscoveryHint")}>
          <button
            onClick={async () => {
              const ok = await confirm({
                title: t("scheduler.purgeConfirmTitle"),
                message: t("scheduler.purgeDiscoveryHint"),
                confirmLabel: t("scheduler.purgeDiscovery"),
                danger: true,
              });
              if (ok) purgeMut.mutate();
            }}
            disabled={purgeMut.isPending}
            className="glass-card glass-hover flex items-center gap-2 px-3 py-2 rounded-xl text-sm disabled:opacity-50 transition"
          >
            <Trash2 className="w-4 h-4" />
            {t("scheduler.purgeDiscovery")}
          </button>
        </Tooltip>
        <Tooltip hint={t("scheduler.pauseHint")}>
          <button
            onClick={() => pauseResume.mutate(data.paused)}
            disabled={pauseResume.isPending}
            className="glass-card glass-hover flex items-center gap-2 px-3 py-2 rounded-xl text-sm disabled:opacity-50 transition"
          >
            {data.paused ? <Play className="w-4 h-4" /> : <Pause className="w-4 h-4" />}
            {data.paused ? t("scheduler.resume") : t("scheduler.pause")}
          </button>
        </Tooltip>
      </div>

      {!data.running_here && (
        <div className="flex items-center gap-2 text-sm bg-amber-500/15 border border-amber-500/30 rounded-xl px-3 py-2">
          <AlertTriangle className="w-4 h-4 text-amber-500 shrink-0" />
          <span className="text-muted">{t("scheduler.notHereNote")}</span>
        </div>
      )}

      {/* Jobs */}
      <div>
        <div className="text-xs uppercase tracking-wide text-muted mb-2">{t("scheduler.jobsTitle")}</div>
        <StatusLegend />
        <div className="space-y-1.5">
          {data.jobs.map((job) => (
            <JobRow
              key={job.id}
              job={job}
              saving={intervalMut.isPending}
              onSave={(minutes) => intervalMut.mutate({ jobId: job.id, minutes })}
              onRun={() => runMut.mutate(job.id)}
              runDisabled={data.paused || runAllMut.isPending}
            />
          ))}
        </div>
      </div>

      {/* Queue */}
      <div>
        <div className="text-xs uppercase tracking-wide text-muted mb-2">{t("scheduler.queueTitle")}</div>
        <div className="grid grid-cols-2 sm:grid-cols-3 gap-2">
          <Stat
            icon={Database}
            label={t("scheduler.queue.recentPending")}
            hint={t("scheduler.queue.recentPendingHint")}
            value={data.queue.channels_recent_pending.toLocaleString()}
          />
          <Stat
            icon={Database}
            label={t("scheduler.queue.deepPending")}
            hint={t("scheduler.queue.deepPendingHint")}
            value={data.queue.channels_deep_pending.toLocaleString()}
          />
          <Stat
            icon={Clock}
            label={t("scheduler.queue.enrichPending")}
            hint={t("scheduler.queue.enrichPendingHint")}
            value={data.queue.videos_pending_enrich.toLocaleString()}
          />
          <Stat
            icon={Clock}
            label={t("scheduler.queue.shortsPending")}
            hint={t("scheduler.queue.shortsPendingHint")}
            value={data.queue.videos_pending_shorts.toLocaleString()}
          />
          <Stat
            icon={RadioTower}
            label={t("scheduler.queue.liveRefresh")}
            hint={t("scheduler.queue.liveRefreshHint")}
            value={data.queue.videos_live_refresh.toLocaleString()}
          />
        </div>
      </div>

      {/* Quota */}
      <div>
        <div className="text-xs uppercase tracking-wide text-muted mb-2">{t("scheduler.quotaTitle")}</div>
        <div className="glass-card rounded-xl p-4">
          <div className="flex items-center gap-2 text-sm">
            <Gauge className="w-4 h-4 text-muted shrink-0" />
            <span>
              {t("scheduler.quotaUsed", {
                used: data.quota.used_today.toLocaleString(),
                budget: data.quota.daily_budget.toLocaleString(),
              })}
            </span>
            <span className="flex-1" />
            <span className="text-muted text-xs">
              {t("scheduler.quotaRemaining", {
                count: data.quota.remaining_today,
                formatted: data.quota.remaining_today.toLocaleString(),
              })}
            </span>
          </div>
          <div className="mt-2 h-2 rounded-full bg-border overflow-hidden">
            <div
              className={`h-full rounded-full ${quotaPct > 90 ? "bg-red-500" : "bg-accent"}`}
              style={{ width: `${quotaPct}%` }}
            />
          </div>
          <div className="text-[11px] text-muted mt-1.5">{t("scheduler.quotaNote")}</div>
        </div>
      </div>

      {/* Maintenance settings */}
      <div>
        <div className="text-xs uppercase tracking-wide text-muted mb-2">{t("scheduler.maintenance.title")}</div>
        <MaintenanceSettings
          m={data.maintenance}
          onSave={(batch) => batchMut.mutate(batch)}
          saving={batchMut.isPending}
        />
      </div>
    </div>
  );
}
