import { ArrowDownToLine, LoaderCircle, Play, X } from "lucide-react";
import { useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
import { useI18n } from "../../../i18n";
import type { Attachment, Post } from "../../../types/post";
import { AttachmentDownloadPill } from "../AttachmentDownloadPill";
import { useVideoPlayer } from "../overlays/VideoPlayer";
import { autolink } from "../utils/autolink";
import { downloadAttachment } from "../utils/downloadFile";
import { formatBytes } from "../utils/formatBytes";
import { postDisplayText } from "../utils/postText";

const MAX_VISIBLE = 4;

function formatDuration(sec: number | undefined): string {
  if (!sec || sec <= 0) return "";
  const m = Math.floor(sec / 60);
  const s = Math.floor(sec % 60);
  return `${m}:${s.toString().padStart(2, "0")}`;
}

function isVideoAttachment(att: Attachment): boolean {
  return att.kind === "video" || att.mime.startsWith("video/");
}

function videoRatio(att: Attachment) {
  return att.width && att.height ? `${att.width} / ${att.height}` : "16 / 9";
}

function VideoAttachmentCard({
  postId,
  attachment,
  compact = false,
  overlayCount,
  onMoreClick,
}: {
  postId: string;
  attachment: Attachment;
  compact?: boolean;
  overlayCount?: number;
  onMoreClick?: () => void;
}) {
  const { openVideo } = useVideoPlayer();
  const [playing, setPlaying] = useState(false);
  const videoRef = useRef<HTMLVideoElement>(null);
  const posterUrl = attachment.posterUrl ?? attachment.thumbnailUrl;
  const duration = formatDuration(attachment.durationSec);
  const previewVideoUrl = attachment.url.includes("#")
    ? attachment.url
    : `${attachment.url}#t=0.1`;

  return (
    <div
      className={`relative w-full overflow-hidden bg-black ${
        compact
          ? "h-full"
          : "max-h-[220px] rounded-xl min-[440px]:max-h-[250px] md:max-h-[300px] lg:max-h-[340px]"
      }`}
      style={compact ? undefined : { aspectRatio: videoRatio(attachment) }}
      onClick={() => {
        if (playing) {
          const v = videoRef.current;
          openVideo(attachment, v?.currentTime ?? 0);
        }
      }}
    >
      {playing && !compact ? (
        <video
          ref={videoRef}
          src={attachment.url}
          poster={attachment.posterUrl}
          controls
          playsInline
          autoPlay
          className="absolute inset-0 h-full w-full"
        />
      ) : (
        <>
          {posterUrl ? (
            <img
              src={posterUrl}
              alt=""
              className={`absolute inset-0 h-full w-full object-cover ${
                overlayCount ? "blur-sm scale-105" : ""
              }`}
            />
          ) : (
            <video
              src={previewVideoUrl}
              preload="metadata"
              muted
              playsInline
              className={`absolute inset-0 h-full w-full object-cover ${
                overlayCount ? "blur-sm scale-105" : ""
              }`}
              aria-hidden="true"
            />
          )}
          {!compact ? (
            <AttachmentDownloadPill
              postId={postId}
              attachment={attachment}
              leadingLabel={duration}
            />
          ) : null}
          {overlayCount ? (
            <button
              type="button"
              onClick={(e) => {
                e.stopPropagation();
                onMoreClick?.();
              }}
              className="absolute inset-0 flex items-center justify-center bg-black/45 text-3xl font-semibold text-white"
              aria-label="Open video list"
            >
              +{overlayCount}
            </button>
          ) : (
            <button
              type="button"
              onClick={(e) => {
                e.stopPropagation();
                if (compact) openVideo(attachment, 0);
                else setPlaying(true);
              }}
              className="absolute inset-0 flex items-center justify-center"
              aria-label="Play video"
            >
              <span
                className={`relative z-10 flex items-center justify-center rounded-full bg-black/55 text-white backdrop-blur ${
                  compact ? "h-9 w-9" : "h-12 w-12 md:h-14 md:w-14"
                }`}
              >
                <Play
                  className={`translate-x-0.5 fill-white ${
                    compact ? "h-4 w-4" : "h-5 w-5 md:h-6 md:w-6"
                  }`}
                />
              </span>
            </button>
          )}
        </>
      )}
    </div>
  );
}

function AttachmentListDownloadButton({
  postId,
  attachment,
}: {
  postId: string;
  attachment: Attachment;
}) {
  const { t } = useI18n();
  const [isDownloading, setIsDownloading] = useState(false);

  const handleDownload = () => {
    if (isDownloading) return;
    setIsDownloading(true);
    void downloadAttachment(postId, attachment.id, attachment.filename)
      .finally(() => setIsDownloading(false))
      .catch(() => {});
  };

  return (
    <button
      type="button"
      onClick={(e) => {
        e.stopPropagation();
        handleDownload();
      }}
      disabled={isDownloading}
      className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-white/10 text-white transition hover:bg-white/20 disabled:cursor-wait"
      aria-label={
        isDownloading ? t("downloading") : `Download ${attachment.filename}`
      }
      aria-busy={isDownloading}
    >
      {isDownloading ? (
        <LoaderCircle className="h-4 w-4 animate-spin" strokeWidth={2.3} />
      ) : (
        <ArrowDownToLine className="h-4 w-4" strokeWidth={2.3} />
      )}
    </button>
  );
}

function VideoListDialog({
  postId,
  videos,
  onClose,
  onPick,
}: {
  postId: string;
  videos: Attachment[];
  onClose: () => void;
  onPick: (attachment: Attachment) => void;
}) {
  useEffect(() => {
    const onKey = (event: KeyboardEvent) => {
      if (event.key === "Escape") onClose();
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);

  return createPortal(
    <div
      className="fixed inset-0 z-[100] flex items-center justify-center bg-black/75 px-4 backdrop-blur-sm"
      onClick={onClose}
      role="dialog"
      aria-modal="true"
      aria-label="Video list"
    >
      <div
        className="w-full max-w-md overflow-hidden rounded-2xl border border-ark-line bg-ark-panel shadow-2xl"
        onClick={(e) => e.stopPropagation()}
      >
        <div className="flex items-center justify-between border-b border-ark-line px-4 py-3">
          <div className="text-sm font-semibold text-neutral-100">选择视频</div>
          <button
            type="button"
            onClick={onClose}
            className="flex h-8 w-8 items-center justify-center rounded-full bg-white/10 text-white transition hover:bg-white/20"
            aria-label="Close"
          >
            <X className="h-4 w-4" />
          </button>
        </div>
        <div className="max-h-[70vh] overflow-y-auto p-2 [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
          {videos.map((video, index) => {
            const thumb = video.posterUrl ?? video.thumbnailUrl;
            const previewVideoUrl = video.url.includes("#")
              ? video.url
              : `${video.url}#t=0.1`;
            const duration = formatDuration(video.durationSec);
            return (
              <div
                key={video.id}
                className="flex w-full items-center gap-3 rounded-xl p-2 text-left transition hover:bg-white/5"
              >
                <button
                  type="button"
                  onClick={() => onPick(video)}
                  className="flex min-w-0 flex-1 items-center gap-3 text-left"
                >
                  <div className="relative h-16 w-24 shrink-0 overflow-hidden rounded-lg bg-black">
                    {thumb ? (
                      <img
                        src={thumb}
                        alt=""
                        className="h-full w-full object-cover"
                      />
                    ) : (
                      <video
                        src={previewVideoUrl}
                        preload="metadata"
                        muted
                        playsInline
                        className="h-full w-full object-cover"
                        aria-hidden="true"
                      />
                    )}
                    <div className="absolute inset-0 flex items-center justify-center bg-black/20 text-white">
                      <Play className="h-4 w-4 fill-white" />
                    </div>
                  </div>
                  <div className="min-w-0 flex-1">
                    <div className="truncate text-sm font-medium text-neutral-100">
                      视频 {index + 1}
                    </div>
                    <div className="mt-1 truncate text-xs text-neutral-400">
                      {[duration, formatBytes(video.sizeBytes)]
                        .filter(Boolean)
                        .join(" · ")}
                    </div>
                  </div>
                </button>
                <AttachmentListDownloadButton
                  postId={postId}
                  attachment={video}
                />
              </div>
            );
          })}
        </div>
      </div>
    </div>,
    document.body,
  );
}

export function VideoBubble({ post }: { post: Post }) {
  const { lang } = useI18n();
  const { openVideo } = useVideoPlayer();
  const [listOpen, setListOpen] = useState(false);
  const videos = post.attachments.filter(isVideoAttachment);
  const text = postDisplayText(post, lang);
  const shouldMerge = videos.length > MAX_VISIBLE;
  if (!videos.length) return null;

  if (shouldMerge) {
    const visible = videos.slice(0, MAX_VISIBLE);
    const extra = videos.length - MAX_VISIBLE;

    return (
      <div className="flex flex-col gap-1.5">
        <div className="grid h-[220px] grid-cols-2 grid-rows-2 gap-[2px] overflow-hidden rounded-xl bg-black min-[440px]:h-[250px] md:h-[300px] lg:h-[340px]">
          {visible.map((att, i) => {
            const isLastSlot = i === MAX_VISIBLE - 1 && extra > 0;
            return (
              <VideoAttachmentCard
                key={att.id}
                postId={post.id}
                attachment={att}
                compact
                overlayCount={isLastSlot ? extra : undefined}
                onMoreClick={() => setListOpen(true)}
              />
            );
          })}
        </div>
        {text ? (
          <div className="message-stream-copyable-text select-text whitespace-pre-wrap break-words text-[14px] leading-snug text-neutral-100">
            {autolink(text)}
          </div>
        ) : null}
        {listOpen ? (
          <VideoListDialog
            postId={post.id}
            videos={videos}
            onClose={() => setListOpen(false)}
            onPick={(att) => {
              setListOpen(false);
              openVideo(att, 0);
            }}
          />
        ) : null}
      </div>
    );
  }

  return (
    <div className="flex flex-col gap-1.5">
      {videos.map((att) => (
        <VideoAttachmentCard key={att.id} postId={post.id} attachment={att} />
      ))}
      {text ? (
        <div className="message-stream-copyable-text select-text whitespace-pre-wrap break-words text-[14px] leading-snug text-neutral-100">
          {autolink(text)}
        </div>
      ) : null}
    </div>
  );
}
