import { useState, useEffect, useCallback, useRef } from "react";
import { useTranslation } from "react-i18next";
import { copyToClipboard } from "@/lib/clipboard";
import { useAdaptivePolling } from "@/hooks/use-adaptive-polling";
import { Input } from "@/components/input";
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/components/tooltip";
import {
  ArrowLeft,
  Check,
  Copy,
  Download,
  Eye,
  FileText,
  Loader2,
  Save,
  ScrollText,
  Search,
  X,
} from "lucide-react";
import { toast } from "sonner";
import {
  getSessionLogs,
  getSessionLogContent,
  getSessionLogBlob,
  getSessionRecordingRetention,
  setSessionRecordingRetention,
  deleteSessionLog,
  type SessionLogRecord,
} from "@/api/session-log-api";
import { SessionRecordingPlayer } from "@/features/session-recording/SessionRecordingPlayer";
import {
  asciicastToPlainText,
  parseAsciicast,
} from "@/features/session-recording/asciicast";

function formatDuration(seconds: number | null): string {
  if (seconds == null) return "--";
  if (seconds < 60) return `${seconds}s`;
  const m = Math.floor(seconds / 60);
  const s = seconds % 60;
  return s > 0 ? `${m}m ${s}s` : `${m}m`;
}

function formatBytes(bytes: number | null): string {
  if (bytes == null || bytes === 0) return "--";
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}

function formatDate(iso: string): string {
  const d = new Date(iso);
  return d.toLocaleString(undefined, {
    month: "short",
    day: "numeric",
    hour: "2-digit",
    minute: "2-digit",
  });
}

function buildFilename(log: SessionLogRecord): string {
  const host = (log.hostName ?? log.hostIp ?? "session")
    .replace(/[^a-zA-Z0-9._-]/g, "_")
    .slice(0, 40);
  const d = new Date(log.startedAt);
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, "0");
  const day = String(d.getDate()).padStart(2, "0");
  const h = String(d.getHours()).padStart(2, "0");
  const min = String(d.getMinutes()).padStart(2, "0");
  const s = String(d.getSeconds()).padStart(2, "0");
  const extension =
    log.format === "guacamole"
      ? "guac"
      : log.format === "asciicast"
        ? "cast"
        : "log";
  return `${host}_${y}-${m}-${day}_${h}-${min}-${s}.${extension}`;
}

function buildTextFilename(log: SessionLogRecord): string {
  return buildFilename(log).replace(/\.(cast|guac|log)$/, ".txt");
}

async function extractPlainText(
  log: SessionLogRecord,
  blob: Blob,
): Promise<string | null> {
  if (log.format === "guacamole") return null;
  const source = await blob.text();
  if (log.format === "text") return source;
  return asciicastToPlainText(parseAsciicast(source));
}

function SectionHeader({ label, count }: { label: string; count: number }) {
  return (
    <div className="flex items-center gap-2 px-3 py-2 border-b border-border/60 bg-muted/20">
      <span className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground/60 flex-1">
        {label}
      </span>
      <span className="text-[10px] font-semibold text-muted-foreground/40 bg-muted/60 px-1.5 py-0.5">
        {count}
      </span>
    </div>
  );
}

function LogRow({
  log,
  onView,
  onDownload,
  onDownloadText,
  onDelete,
}: {
  log: SessionLogRecord;
  onView: () => void;
  onDownload: () => void;
  onDownloadText: () => void;
  onDelete: () => void;
}) {
  const { t } = useTranslation();
  const hostLabel = log.hostName ?? log.hostIp ?? `Host ${log.hostId}`;

  return (
    <div className="group flex items-center gap-2.5 px-3 py-2.5 border-b border-border/40 last:border-b-0 hover:bg-muted/40 transition-colors">
      <div className="shrink-0 flex items-center justify-center size-7 bg-muted/60 text-muted-foreground">
        <ScrollText className="size-3.5" />
      </div>

      <div className="flex flex-col flex-1 min-w-0 gap-0.5">
        <span className="text-xs font-semibold truncate text-foreground">
          {hostLabel}
        </span>
        <span className="text-[10px] text-muted-foreground/60 truncate">
          {formatDate(log.startedAt)}
          {" · "}
          {(log.protocol ?? "ssh").toUpperCase()}
          {log.username ? ` · ${log.username}` : ""}
          {" · "}
          {formatDuration(log.duration)}
          {" · "}
          {formatBytes(log.sizeBytes)}
        </span>
      </div>

      <TooltipProvider>
        <div className="shrink-0 flex items-center gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity">
          <Tooltip>
            <TooltipTrigger asChild>
              <button
                onClick={onView}
                className="size-6 flex items-center justify-center text-muted-foreground/50 hover:text-foreground hover:bg-muted/60 transition-colors"
              >
                <Eye className="size-3" />
              </button>
            </TooltipTrigger>
            <TooltipContent side="left">View log</TooltipContent>
          </Tooltip>
          {log.format === "asciicast" && (
            <Tooltip>
              <TooltipTrigger asChild>
                <button
                  onClick={onDownloadText}
                  className="size-6 flex items-center justify-center text-muted-foreground/50 hover:text-foreground hover:bg-muted/60 transition-colors"
                >
                  <FileText className="size-3" />
                </button>
              </TooltipTrigger>
              <TooltipContent side="left">
                {t("sessionLogs.downloadAsText")}
              </TooltipContent>
            </Tooltip>
          )}
          <Tooltip>
            <TooltipTrigger asChild>
              <button
                onClick={onDownload}
                className="size-6 flex items-center justify-center text-muted-foreground/50 hover:text-foreground hover:bg-muted/60 transition-colors"
              >
                <Download className="size-3" />
              </button>
            </TooltipTrigger>
            <TooltipContent side="left">Download</TooltipContent>
          </Tooltip>
          <Tooltip>
            <TooltipTrigger asChild>
              <button
                onClick={onDelete}
                className="size-6 flex items-center justify-center text-muted-foreground/50 hover:text-destructive hover:bg-destructive/10 transition-colors"
              >
                <X className="size-3" />
              </button>
            </TooltipTrigger>
            <TooltipContent side="left">Delete</TooltipContent>
          </Tooltip>
        </div>
      </TooltipProvider>
    </div>
  );
}

export function SessionLogsPanel() {
  const { t } = useTranslation();
  const [logs, setLogs] = useState<SessionLogRecord[]>([]);
  const [loading, setLoading] = useState(true);
  const [filter, setFilter] = useState("");
  const [viewLog, setViewLog] = useState<SessionLogRecord | null>(null);
  const [viewContent, setViewContent] = useState<string>("");
  const [viewBlob, setViewBlob] = useState<Blob | null>(null);
  const [viewText, setViewText] = useState<string | null>(null);
  const [viewLoading, setViewLoading] = useState(false);
  const [deleteTarget, setDeleteTarget] = useState<SessionLogRecord | null>(
    null,
  );
  const [deleting, setDeleting] = useState(false);
  const [copied, setCopied] = useState(false);
  const [retentionDays, setRetentionDays] = useState<number | null>(null);
  const [savingRetention, setSavingRetention] = useState(false);
  const logsRef = useRef(logs);
  logsRef.current = logs;

  const load = useCallback(
    async (initial = false) => {
      if (initial) setLoading(true);
      try {
        const fresh = await getSessionLogs();
        const changed =
          logsRef.current.length !== fresh.length ||
          logsRef.current.some((log, i) => log.id !== fresh[i]?.id);
        if (changed) {
          logsRef.current = fresh;
          setLogs(fresh);
        }
        return changed;
      } catch (error) {
        if (initial) {
          toast.error(t("sessionLogs.loadError"));
          return false;
        }
        throw error;
      } finally {
        if (initial) setLoading(false);
      }
    },
    [t],
  );

  useEffect(() => {
    void load(true);
    getSessionRecordingRetention()
      .then(setRetentionDays)
      .catch(() => setRetentionDays(null));
  }, [load]);

  useAdaptivePolling(
    () => load(false),
    {
      minIntervalMs: 5_000,
      maxIntervalMs: 30_000,
      stablePollsPerStep: 3,
    },
    true,
    { runImmediately: false },
  );

  const q = filter.trim().toLowerCase();
  const filtered = q
    ? logs.filter((l) =>
        `${l.hostName ?? ""} ${l.hostIp ?? ""} ${l.username ?? ""} ${l.protocol}`
          .toLowerCase()
          .includes(q),
      )
    : logs;

  const handleView = async (log: SessionLogRecord) => {
    setViewLog(log);
    setViewContent("");
    setViewBlob(null);
    setViewText(null);
    setViewLoading(true);
    try {
      if (log.format === "text") {
        const text = await getSessionLogContent(log.id);
        setViewContent(text);
        setViewText(text);
      } else {
        const blob = await getSessionLogBlob(log.id);
        setViewBlob(blob);
        setViewText(await extractPlainText(log, blob));
      }
    } catch {
      toast.error(t("sessionLogs.loadError"));
    } finally {
      setViewLoading(false);
    }
  };

  const downloadBlob = (blob: Blob, filename: string) => {
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = filename;
    a.click();
    URL.revokeObjectURL(url);
  };

  const handleDownload = async (log: SessionLogRecord) => {
    try {
      downloadBlob(await getSessionLogBlob(log.id), buildFilename(log));
    } catch {
      toast.error(t("sessionLogs.loadError"));
    }
  };

  const handleDownloadText = async (log: SessionLogRecord) => {
    try {
      const text =
        log.id === viewLog?.id && viewText != null
          ? viewText
          : await extractPlainText(log, await getSessionLogBlob(log.id));
      if (text == null) {
        toast.error(t("sessionLogs.loadError"));
        return;
      }
      downloadBlob(
        new Blob([text], { type: "text/plain" }),
        buildTextFilename(log),
      );
    } catch {
      toast.error(t("sessionLogs.loadError"));
    }
  };

  const handleDelete = async () => {
    if (!deleteTarget) return;
    setDeleting(true);
    try {
      await deleteSessionLog(deleteTarget.id);
      setLogs((prev) => prev.filter((l) => l.id !== deleteTarget.id));
      setDeleteTarget(null);
    } catch {
      toast.error(t("sessionLogs.deleteError"));
    } finally {
      setDeleting(false);
    }
  };

  const handleCopy = async () => {
    if (!viewText) return;
    const ok = await copyToClipboard(viewText);
    if (ok) {
      setCopied(true);
      setTimeout(() => setCopied(false), 1500);
    } else {
      toast.error(t("common.copyFailed"));
    }
  };

  if (loading) {
    return (
      <div className="flex items-center justify-center flex-1 p-8">
        <Loader2 className="size-5 text-muted-foreground animate-spin" />
      </div>
    );
  }

  // Inline log viewer
  if (viewLog) {
    const hostLabel =
      viewLog.hostName ?? viewLog.hostIp ?? `Host ${viewLog.hostId}`;
    return (
      <div className="flex flex-col flex-1 min-h-0 overflow-hidden">
        {/* Viewer header */}
        <div className="flex items-center gap-2 px-2 py-2 border-b border-border/60 bg-muted/20 shrink-0">
          <button
            onClick={() => setViewLog(null)}
            className="size-6 flex items-center justify-center text-muted-foreground/50 hover:text-foreground hover:bg-muted/60 transition-colors"
          >
            <ArrowLeft className="size-3.5" />
          </button>
          <div className="flex flex-col flex-1 min-w-0">
            <span className="text-xs font-semibold truncate text-foreground">
              {hostLabel}
            </span>
            <span className="text-[10px] text-muted-foreground/50">
              {formatDate(viewLog.startedAt)}
              {` · ${viewLog.protocol.toUpperCase()}`}
              {viewLog.duration != null
                ? ` · ${formatDuration(viewLog.duration)}`
                : ""}
              {" · "}
              {formatBytes(viewLog.sizeBytes)}
            </span>
          </div>
          <TooltipProvider>
            <div className="flex items-center gap-0.5 shrink-0">
              {viewText != null && (
                <Tooltip>
                  <TooltipTrigger asChild>
                    <button
                      onClick={handleCopy}
                      className="size-6 flex items-center justify-center text-muted-foreground/50 hover:text-foreground hover:bg-muted/60 transition-colors"
                    >
                      {copied ? (
                        <Check className="size-3 text-green-500" />
                      ) : (
                        <Copy className="size-3" />
                      )}
                    </button>
                  </TooltipTrigger>
                  <TooltipContent side="left">
                    {copied
                      ? t("sessionLogs.copied")
                      : t("sessionLogs.copyContent")}
                  </TooltipContent>
                </Tooltip>
              )}
              {viewLog.format === "asciicast" && (
                <Tooltip>
                  <TooltipTrigger asChild>
                    <button
                      onClick={() => handleDownloadText(viewLog)}
                      className="size-6 flex items-center justify-center text-muted-foreground/50 hover:text-foreground hover:bg-muted/60 transition-colors"
                    >
                      <FileText className="size-3" />
                    </button>
                  </TooltipTrigger>
                  <TooltipContent side="left">
                    {t("sessionLogs.downloadAsText")}
                  </TooltipContent>
                </Tooltip>
              )}
              <Tooltip>
                <TooltipTrigger asChild>
                  <button
                    onClick={() => handleDownload(viewLog)}
                    className="size-6 flex items-center justify-center text-muted-foreground/50 hover:text-foreground hover:bg-muted/60 transition-colors"
                  >
                    <Download className="size-3" />
                  </button>
                </TooltipTrigger>
                <TooltipContent side="left">
                  {t("sessionLogs.downloadLog")}
                </TooltipContent>
              </Tooltip>
            </div>
          </TooltipProvider>
        </div>

        {/* Log content */}
        <div className="flex-1 min-h-0 overflow-y-auto">
          {viewLoading ? (
            <div className="flex items-center justify-center py-16">
              <Loader2 className="size-4 animate-spin text-muted-foreground" />
            </div>
          ) : viewBlob ? (
            <SessionRecordingPlayer log={viewLog} blob={viewBlob} />
          ) : (
            <pre className="p-3 text-[11px] font-mono whitespace-pre-wrap break-all text-foreground/80 leading-relaxed">
              {viewContent || "(empty)"}
            </pre>
          )}
        </div>
      </div>
    );
  }

  return (
    <div className="flex flex-col flex-1 min-h-0">
      <div className="flex-1 overflow-y-auto">
        {retentionDays != null && (
          <div className="flex items-center gap-2 border-b border-border/60 px-3 py-2 text-[10px] text-muted-foreground">
            <span className="flex-1">Recording retention</span>
            <Input
              type="number"
              min={1}
              max={3650}
              value={retentionDays}
              onChange={(event) => setRetentionDays(Number(event.target.value))}
              className="h-7 w-20 text-xs"
              aria-label="Recording retention days"
            />
            <span>days</span>
            <button
              type="button"
              disabled={savingRetention}
              onClick={async () => {
                setSavingRetention(true);
                try {
                  await setSessionRecordingRetention(retentionDays);
                  toast.success("Recording retention updated");
                } catch {
                  toast.error("Failed to update recording retention");
                } finally {
                  setSavingRetention(false);
                }
              }}
              className="flex size-7 items-center justify-center hover:bg-muted disabled:opacity-50"
              aria-label="Save recording retention"
            >
              {savingRetention ? (
                <Loader2 className="size-3 animate-spin" />
              ) : (
                <Save className="size-3" />
              )}
            </button>
          </div>
        )}
        {logs.length === 0 ? (
          <div className="flex flex-col items-center justify-center flex-1 gap-3 p-6 text-center py-16">
            <div className="size-10 bg-muted/40 flex items-center justify-center">
              <ScrollText className="size-5 text-muted-foreground/30" />
            </div>
            <div className="flex flex-col gap-1">
              <span className="text-sm font-semibold text-muted-foreground/60">
                {t("sessionLogs.noLogs")}
              </span>
              <span className="text-xs text-muted-foreground/40">
                {t("sessionLogs.noLogsDesc")}
              </span>
            </div>
          </div>
        ) : (
          <>
            <div className="relative px-3 py-2 border-b border-border/60">
              <Search className="absolute left-5.5 top-1/2 -translate-y-1/2 size-3.5 text-muted-foreground/50 pointer-events-none" />
              <Input
                placeholder={t("sessionLogs.filterByHost")}
                value={filter}
                onChange={(e) => setFilter(e.target.value)}
                className="pl-8 h-7 text-xs"
              />
            </div>

            {filtered.length === 0 ? (
              <div className="flex flex-col items-center justify-center gap-2 py-10 text-center">
                <span className="text-xs text-muted-foreground/50">
                  No results for &quot;{filter}&quot;
                </span>
              </div>
            ) : (
              <div className="flex flex-col">
                <SectionHeader
                  label={t("sessionLogs.title")}
                  count={filtered.length}
                />
                {filtered.map((log) => (
                  <LogRow
                    key={log.id}
                    log={log}
                    onView={() => handleView(log)}
                    onDownload={() => handleDownload(log)}
                    onDownloadText={() => handleDownloadText(log)}
                    onDelete={() => setDeleteTarget(log)}
                  />
                ))}
              </div>
            )}
          </>
        )}
      </div>

      {/* Inline delete confirmation - positioned against the relative parent in AppShell */}
      {deleteTarget && (
        <div className="absolute inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-sm p-4">
          <div className="bg-popover border border-border shadow-xl w-full max-w-xs flex flex-col gap-4 p-4">
            <p className="text-sm text-foreground">
              {t("sessionLogs.confirmDelete")}
            </p>
            <p className="text-xs text-muted-foreground">
              {deleteTarget.hostName ??
                deleteTarget.hostIp ??
                `Session ${deleteTarget.id}`}
              {" · "}
              {formatDate(deleteTarget.startedAt)}
            </p>
            <div className="flex justify-end gap-2">
              <button
                onClick={() => setDeleteTarget(null)}
                className="px-3 py-1.5 text-xs border border-border text-muted-foreground hover:text-foreground hover:bg-muted transition-colors"
              >
                {t("common.cancel")}
              </button>
              <button
                onClick={handleDelete}
                disabled={deleting}
                className="px-3 py-1.5 text-xs bg-destructive text-destructive-foreground hover:bg-destructive/90 transition-colors flex items-center gap-1.5"
              >
                {deleting && <Loader2 className="size-3 animate-spin" />}
                {t("sessionLogs.deleteLog")}
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}
