import { memo, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import {
  Bookmark,
  Check,
  CheckCheck,
  Eye,
  EyeOff,
  Play,
  RotateCcw,
} from "lucide-react";
import Avatar from "./Avatar";
import AddToPlaylist from "./AddToPlaylist";
import DownloadButton from "./DownloadButton";
import clsx from "clsx";
import type { Video } from "../lib/api";
import { formatDate, formatDuration, formatViews, relativeTime } from "../lib/format";

function Actions({
  video,
  onState,
  onResetState,
  onToggleSave,
}: {
  video: Video;
  onState: (id: string, status: string) => void;
  onResetState?: (id: string) => void;
  onToggleSave: (id: string, saved: boolean) => void;
}) {
  const { t } = useTranslation();
  const act = (status: string) => (e: React.MouseEvent) => {
    e.preventDefault();
    e.stopPropagation();
    onState(video.id, video.status === status ? "new" : status);
  };
  // Pristine = never opened: default status and no resume position. The reset clears the
  // whole state (incl. an in-progress position the un-watch toggle can't touch).
  const resettable = video.status !== "new" || video.position_seconds > 0;
  const toggleSave = (e: React.MouseEvent) => {
    e.preventDefault();
    e.stopPropagation();
    onToggleSave(video.id, !video.saved);
  };
  return (
    <div className="flex gap-1 opacity-0 group-hover:opacity-100 transition">
      <button
        onClick={act("watched")}
        title={video.status === "watched" ? t("card.watchedUnmark") : t("card.markWatched")}
        className={clsx(
          "p-1.5 rounded-md hover:bg-surface text-muted hover:text-fg",
          video.status === "watched" && "text-accent"
        )}
      >
        {video.status === "watched" ? (
          <CheckCheck className="w-4 h-4" />
        ) : (
          <Check className="w-4 h-4" />
        )}
      </button>
      <button
        onClick={toggleSave}
        title={video.saved ? t("card.savedRemove") : t("card.saveForLater")}
        className={clsx(
          "p-1.5 rounded-md hover:bg-surface text-muted hover:text-fg",
          video.saved && "fill-current text-accent"
        )}
      >
        <Bookmark className="w-4 h-4" />
      </button>
      <AddToPlaylist videoId={video.id} />
      <DownloadButton videoId={video.id} title={video.title} />
      <button
        onClick={act("hidden")}
        title={video.status === "hidden" ? t("card.unhide") : t("card.hide")}
        className={clsx(
          "p-1.5 rounded-md hover:bg-surface text-muted hover:text-fg",
          video.status === "hidden" && "text-accent"
        )}
      >
        {video.status === "hidden" ? (
          <Eye className="w-4 h-4" />
        ) : (
          <EyeOff className="w-4 h-4" />
        )}
      </button>
      {onResetState && resettable && (
        <button
          onClick={(e) => {
            e.preventDefault();
            e.stopPropagation();
            onResetState(video.id);
          }}
          title={t("card.resetState")}
          className="p-1.5 rounded-md hover:bg-surface text-muted hover:text-fg"
        >
          <RotateCcw className="w-4 h-4" />
        </button>
      )}
    </div>
  );
}

// Plain left-click opens the in-app player (the experiment); Ctrl/Cmd/middle-click
// keep the native "open youtube.com in a new tab" behavior via the underlying href.
function openInApp(
  e: React.MouseEvent,
  video: Video,
  onOpen?: (v: Video, startAt?: number | null) => void
): void {
  if (!onOpen || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0) return;
  e.preventDefault();
  onOpen(video);
}

function Thumb({
  video,
  className,
  onOpen,
}: {
  video: Video;
  className?: string;
  onOpen?: (v: Video, startAt?: number | null) => void;
}) {
  const { t } = useTranslation();
  // A non-zero saved position on an unfinished video = "in progress": show a resume
  // progress bar and offer Continue / Restart instead of a plain Play.
  const inProgress = video.position_seconds > 0 && video.status !== "watched";
  const pct =
    inProgress && video.duration_seconds
      ? Math.min(99, Math.max(2, (video.position_seconds / video.duration_seconds) * 100))
      : 0;

  // Open in the in-app player at an explicit position (null = resume from saved).
  const open = (startAt: number | null) => (e: React.MouseEvent) => {
    e.preventDefault();
    e.stopPropagation();
    onOpen?.(video, startAt);
  };

  return (
    <a
      href={video.watch_url}
      target="_blank"
      rel="noreferrer"
      onClick={(e) => openInApp(e, video, onOpen)}
      className={clsx(
        "block relative rounded-xl overflow-hidden bg-surface border border-border shadow-md group-hover:shadow-2xl transition-shadow",
        className
      )}
    >
      {video.thumbnail_url ? (
        <img
          src={video.thumbnail_url}
          alt=""
          loading="lazy"
          decoding="async"
          width={1280}
          height={720}
          className="w-full h-full object-cover"
        />
      ) : (
        <div className="w-full h-full" />
      )}
      {video.duration_seconds != null ? (
        <span className="absolute bottom-1.5 right-1.5 bg-black/80 text-white text-[11px] font-medium px-1.5 py-0.5 rounded">
          {formatDuration(video.duration_seconds)}
        </span>
      ) : video.live_status === "live" || video.live_status === "upcoming" ? (
        <span
          className={`absolute bottom-1.5 right-1.5 text-[10px] font-semibold uppercase tracking-wide px-1.5 py-0.5 rounded ${
            video.live_status === "live" ? "bg-red-500/90 text-white" : "bg-black/80 text-white"
          }`}
        >
          {t(`card.${video.live_status}`)}
        </span>
      ) : null}
      {video.live_status === "was_live" && (
        <span className="absolute top-1.5 left-1.5 bg-accent text-accent-fg text-[10px] font-semibold uppercase tracking-wide px-1.5 py-0.5 rounded">
          {t("card.stream")}
        </span>
      )}
      {video.saved && (
        <span className="absolute top-1.5 right-1.5 bg-accent text-accent-fg rounded-full p-1">
          <Bookmark className="w-3.5 h-3.5" />
        </span>
      )}
      {/* Hover overlay: Play everywhere; Continue + Restart once the video is in progress. */}
      {onOpen && (
        <div className="absolute inset-0 flex items-center justify-center gap-2 bg-black/45 opacity-0 group-hover:opacity-100 transition-opacity">
          {inProgress ? (
            <>
              <button
                onClick={open(null)}
                title={t("card.continueTitle")}
                className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm font-semibold bg-accent text-accent-fg shadow-lg hover:opacity-90 transition"
              >
                <Play className="w-4 h-4 fill-current" />
                {t("card.continue")}
              </button>
              <button
                onClick={open(0)}
                title={t("card.restartTitle")}
                className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm font-medium bg-white/15 text-white backdrop-blur-sm hover:bg-white/25 transition"
              >
                <RotateCcw className="w-4 h-4" />
                {t("card.restart")}
              </button>
            </>
          ) : (
            <button
              onClick={open(null)}
              title={t("card.play")}
              className="inline-flex items-center justify-center w-12 h-12 rounded-full bg-accent text-accent-fg shadow-lg hover:scale-105 transition"
            >
              <Play className="w-5 h-5 fill-current translate-x-[1px]" />
            </button>
          )}
        </div>
      )}
      {/* Resume progress bar (sits at the very bottom, under the duration badge). */}
      {pct > 0 && (
        <div className="absolute bottom-0 left-0 right-0 h-1 bg-black/40">
          <div className="h-full bg-accent" style={{ width: `${pct}%` }} />
        </div>
      )}
    </a>
  );
}

function VideoCard({
  video,
  view,
  onState,
  onResetState,
  onToggleSave,
  onOpenChannel,
  onOpen,
}: {
  video: Video;
  view: "grid" | "list";
  onState: (id: string, status: string) => void;
  onResetState?: (id: string) => void;
  onToggleSave: (id: string, saved: boolean) => void;
  onOpenChannel?: (channelId: string, channelName: string) => void;
  onOpen?: (v: Video, startAt?: number | null) => void;
}) {
  const { t, i18n } = useTranslation();
  const watched = video.status === "watched";
  const meta = (
    <>
      {video.view_count != null && (
        <>
          <span title={`${video.view_count.toLocaleString()} ${t("card.views")}`}>
            {formatViews(video.view_count)} {t("card.views")}
          </span>{" "}
          ·{" "}
        </>
      )}
      {relativeTime(video.published_at)}
      {video.published_at && <>{" · "}{formatDate(video.published_at, i18n.language)}</>}
    </>
  );
  // Show the full title in a native tooltip only when it's clamped (overflows its 2 lines).
  const titleRef = useRef<HTMLAnchorElement>(null);
  const [titleClamped, setTitleClamped] = useState(false);
  useEffect(() => {
    const el = titleRef.current;
    if (!el) return;
    const check = () => setTitleClamped(el.scrollHeight > el.clientHeight + 1);
    check();
    const ro = new ResizeObserver(check);
    ro.observe(el);
    return () => ro.disconnect();
  }, [video.title]);
  const title = (
    <a
      ref={titleRef}
      href={video.watch_url}
      target="_blank"
      rel="noreferrer"
      onClick={(e) => openInApp(e, video, onOpen)}
      title={titleClamped ? video.title ?? undefined : undefined}
      className="font-medium leading-snug line-clamp-2 hover:text-accent"
    >
      {video.title}
    </a>
  );
  // Title + channel button + meta — identical in both layouts (only their wrapper / Actions placement
  // differs). Safe to reuse the same element: exactly one of the two branches below renders.
  const textBlock = (
    <>
      {title}
      <button
        onClick={(e) => {
          e.stopPropagation();
          onOpenChannel?.(video.channel_id, video.channel_title ?? t("card.thisChannel"));
        }}
        className="text-sm text-muted truncate mt-0.5 block w-fit max-w-full text-left hover:text-fg"
      >
        {video.channel_title}
      </button>
      <div className="text-xs text-muted mt-0.5">{meta}</div>
    </>
  );

  if (view === "list") {
    return (
      <div
        className={clsx(
          "cv-row group flex gap-3 p-2 rounded-xl hover:bg-card hover:shadow-lg transition",
          watched && "opacity-55"
        )}
      >
        <Thumb video={video} className="w-44 aspect-video shrink-0" onOpen={onOpen} />
        <div className="min-w-0 flex-1">{textBlock}</div>
        <Actions video={video} onState={onState} onResetState={onResetState} onToggleSave={onToggleSave} />
      </div>
    );
  }

  return (
    <div
      className={clsx(
        "cv-card group glass-card glass-hover rounded-2xl p-2.5 transition-all duration-150 hover:-translate-y-1",
        watched && "opacity-55"
      )}
    >
      <Thumb video={video} className="aspect-video" onOpen={onOpen} />
      <div className="flex gap-3 mt-2.5 px-0.5 pb-0.5">
        <Avatar
          src={video.channel_thumbnail}
          fallback={video.channel_title ?? ""}
          className="w-9 h-9 rounded-full shrink-0 mt-0.5"
        />
        <div className="min-w-0 flex-1">
          {textBlock}
          <Actions video={video} onState={onState} onResetState={onResetState} onToggleSave={onToggleSave} />
        </div>
      </div>
    </div>
  );
}

// Memoized so appending a feed page only renders the new cards, not every existing
// one (the callbacks from Feed are stable; non-overridden video objects keep their
// identity across renders).
export default memo(VideoCard);
