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

const MAX_VISIBLE = 4;

function videoGridClass(count: number) {
  const height = "h-[230px] min-[440px]:h-[250px] md:h-[300px] lg:h-[340px]";
  if (count === 2) return `${height} grid grid-cols-1 grid-rows-2`;
  return `${height} grid grid-cols-2 grid-rows-2`;
}

function videoItemClass(index: number, count: number) {
  if (count === 3 && index === 0) return "row-span-2";
  return "";
}

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 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"
          : "h-[180px] min-[440px]:h-[210px] md:h-[260px] lg:h-[300px]"
      }`}
      style={compact ? undefined : { aspectRatio: videoRatio(attachment) }}
    >
      {playing && !compact ? (
        <MessageInlineVideo
          postId={postId}
          attachment={attachment}
          leadingLabel={duration}
        />
      ) : (
        <>
          {posterUrl ? (
            <img
              src={posterUrl}
              alt=""
              loading="lazy"
              decoding="async"
              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"
            />
          )}
          {!overlayCount ? (
            <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, undefined, postId);
                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 { showToast } = useToast();
  const [isDownloading, setIsDownloading] = useState(false);

  const handleDownload = async () => {
    if (isDownloading) return;
    setIsDownloading(true);
    try {
      await downloadAttachment(postId, attachment.id, attachment.filename);
    } catch {
      showToast(t("downloadFail"), "error");
    } finally {
      setIsDownloading(false);
    }
  };

  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} />
      ) : (
        <DownloadCloudIcon className="h-4 w-4" />
      )}
    </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=""
                        loading="lazy"
                        decoding="async"
                        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);
  if (!videos.length) return null;

  if (videos.length >= 2) {
    const visible = videos.slice(0, MAX_VISIBLE);
    const extra = videos.length - MAX_VISIBLE;
    const layoutCount = Math.min(videos.length, MAX_VISIBLE);

    return (
      <div className="flex flex-col">
        <div
          className={`${videoGridClass(layoutCount)} gap-px overflow-hidden bg-black`}
        >
          {visible.map((att, i) => {
            const isLastSlot = i === MAX_VISIBLE - 1 && extra > 0;
            return (
              <div
                key={att.id}
                className={`h-full w-full ${videoItemClass(i, layoutCount)}`}
              >
                <VideoAttachmentCard
                  postId={post.id}
                  attachment={att}
                  compact
                  overlayCount={isLastSlot ? extra : undefined}
                  onMoreClick={() => setListOpen(true)}
                />
              </div>
            );
          })}
        </div>
        {text ? (
          <CollapsibleText
            wrapperClassName="px-4 pt-3"
            className="message-stream-copyable-text select-text whitespace-pre-wrap break-words text-[14px] leading-6 text-neutral-100"
          >
            {autolink(text)}
          </CollapsibleText>
        ) : null}
        {listOpen ? (
          <VideoListDialog
            postId={post.id}
            videos={videos}
            onClose={() => setListOpen(false)}
            onPick={(att) => {
              setListOpen(false);
              openVideo(att, 0, undefined, post.id);
            }}
          />
        ) : null}
      </div>
    );
  }

  return (
    <div className="flex flex-col">
      <VideoAttachmentCard postId={post.id} attachment={videos[0]} />
      {text ? (
        <CollapsibleText
          wrapperClassName="px-4 pt-3"
          className="message-stream-copyable-text select-text whitespace-pre-wrap break-words text-[14px] leading-6 text-neutral-100"
        >
          {autolink(text)}
        </CollapsibleText>
      ) : null}
    </div>
  );
}
