import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useRef,
  useState,
  type PropsWithChildren,
} from "react";
import { createPortal } from "react-dom";
import { ChevronLeft, ChevronRight, LoaderCircle, X } from "lucide-react";
import type { Attachment } from "../../../types/post";
import { DownloadCloudIcon } from "../../icons/DownloadCloudIcon";
import { useI18n } from "../../../i18n";
import { useToast } from "../../Toast";
import { BubbleImage } from "../BubbleImage";
import { autolink } from "../utils/autolink";
import { downloadAttachment } from "../utils/downloadFile";

type LightboxState = {
  images: Attachment[];
  index: number;
  caption?: string;
  postId?: string;
} | null;

type Ctx = {
  openLightbox: (
    images: Attachment[],
    startIndex?: number,
    caption?: string,
    postId?: string,
  ) => void;
  closeLightbox: () => void;
};

const LightboxContext = createContext<Ctx | null>(null);

export function useLightbox(): Ctx {
  const ctx = useContext(LightboxContext);
  if (!ctx)
    throw new Error("useLightbox must be used inside ImageLightboxProvider");
  return ctx;
}

export function ImageLightboxProvider({ children }: PropsWithChildren) {
  const [state, setState] = useState<LightboxState>(null);

  const openLightbox = useCallback(
    (
      images: Attachment[],
      startIndex = 0,
      caption?: string,
      postId?: string,
    ) => {
      if (!images.length) return;
      const i = Math.min(Math.max(0, startIndex), images.length - 1);
      setState({ images, index: i, caption, postId });
    },
    [],
  );

  const closeLightbox = useCallback(() => setState(null), []);

  return (
    <LightboxContext.Provider value={{ openLightbox, closeLightbox }}>
      {children}
      {state ? (
        <LightboxView
          images={state.images}
          startIndex={state.index}
          caption={state.caption}
          postId={state.postId}
          onClose={closeLightbox}
        />
      ) : null}
    </LightboxContext.Provider>
  );
}

function LightboxDownloadButton({
  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);
      showToast(t("downloadOk"));
    } catch {
      showToast(t("downloadFail"), "error");
    } finally {
      setIsDownloading(false);
    }
  };

  return (
    <button
      type="button"
      onClick={(e) => {
        e.stopPropagation();
        handleDownload();
      }}
      disabled={isDownloading}
      className="flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white transition hover:bg-white/20 disabled:cursor-wait"
      aria-label={isDownloading ? t("downloading") : t("download")}
      aria-busy={isDownloading}
    >
      {isDownloading ? (
        <LoaderCircle className="h-5 w-5 animate-spin" strokeWidth={2.3} />
      ) : (
        <DownloadCloudIcon className="h-5 w-5" />
      )}
    </button>
  );
}

function Filmstrip({
  images,
  index,
  onSelect,
}: {
  images: Attachment[];
  index: number;
  onSelect: (i: number) => void;
}) {
  const activeRef = useRef<HTMLButtonElement | null>(null);

  useEffect(() => {
    activeRef.current?.scrollIntoView({
      behavior: "smooth",
      block: "nearest",
      inline: "center",
    });
  }, [index]);

  return (
    <div
      className="shrink-0 bg-gradient-to-t from-black/90 to-transparent pb-[max(env(safe-area-inset-bottom),0.75rem)] pt-3"
      onClick={(e) => e.stopPropagation()}
    >
      <div className="mx-auto flex max-w-[920px] gap-2 overflow-x-auto px-4 [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
        {images.map((image, i) => {
          const active = i === index;
          return (
            <button
              key={image.id}
              ref={active ? activeRef : null}
              type="button"
              onClick={(e) => {
                e.stopPropagation();
                onSelect(i);
              }}
              className={`relative h-14 w-14 shrink-0 overflow-hidden rounded-lg bg-black transition duration-200 md:h-16 md:w-16 ${
                active
                  ? "opacity-100 ring-2 ring-white"
                  : "opacity-45 hover:opacity-80"
              }`}
              aria-label={`Image ${i + 1}`}
              aria-current={active}
            >
              <BubbleImage
                src={image.thumbnailUrl ?? image.thumbUrl ?? image.url}
                className="h-full w-full object-cover"
              />
            </button>
          );
        })}
      </div>
    </div>
  );
}

function LightboxView({
  images,
  startIndex,
  caption: captionText,
  postId,
  onClose,
}: {
  images: Attachment[];
  startIndex: number;
  caption?: string;
  postId?: string;
  onClose: () => void;
}) {
  const [index, setIndex] = useState(startIndex);
  const [isCaptionVisible, setIsCaptionVisible] = useState(true);
  const touchStartX = useRef<number | null>(null);

  const goPrev = useCallback(
    () => setIndex((i) => (i - 1 + images.length) % images.length),
    [images.length],
  );
  const goNext = useCallback(
    () => setIndex((i) => (i + 1) % images.length),
    [images.length],
  );

  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (e.key === "Escape") onClose();
      if (e.key === "ArrowLeft") goPrev();
      if (e.key === "ArrowRight") goNext();
    };
    window.addEventListener("keydown", onKey);
    const prevOverflow = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => {
      window.removeEventListener("keydown", onKey);
      document.body.style.overflow = prevOverflow;
    };
  }, [goPrev, goNext, onClose]);

  useEffect(() => {
    setIsCaptionVisible(true);
  }, [captionText, index]);

  const current = images[index];
  const caption = captionText?.trim();
  const showCaption = !!caption && isCaptionVisible;
  const hasMany = images.length > 1;
  if (!current) return null;

  return createPortal(
    <div
      className="fixed inset-0 z-[100] flex flex-col bg-black/95 backdrop-blur-sm"
      onClick={onClose}
      role="dialog"
      aria-modal="true"
    >
      {/* Top bar: counter + actions */}
      <div
        className="relative z-20 flex shrink-0 items-center justify-between px-4 pb-2 pt-[max(env(safe-area-inset-top),1rem)]"
        onClick={(e) => e.stopPropagation()}
      >
        <div className="flex h-10 items-center">
          {hasMany ? (
            <span className="rounded-full bg-white/10 px-3 py-1 text-xs font-medium text-white">
              {index + 1} / {images.length}
            </span>
          ) : null}
        </div>
        <div className="flex items-center gap-2">
          {postId ? (
            <LightboxDownloadButton postId={postId} attachment={current} />
          ) : null}
          <button
            type="button"
            onClick={onClose}
            className="flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white transition hover:bg-white/20"
            aria-label="Close"
          >
            <X className="h-5 w-5" />
          </button>
        </div>
      </div>

      {/* Image stage */}
      <div className="relative flex min-h-0 w-full flex-1 items-center justify-center">
        {hasMany ? (
          <>
            <button
              type="button"
              onClick={(e) => {
                e.stopPropagation();
                goPrev();
              }}
              className="absolute left-2 top-1/2 z-10 flex h-12 w-12 -translate-y-1/2 items-center justify-center rounded-full bg-white/10 text-white transition hover:bg-white/20 md:left-6"
              aria-label="Previous"
            >
              <ChevronLeft className="h-6 w-6" />
            </button>
            <button
              type="button"
              onClick={(e) => {
                e.stopPropagation();
                goNext();
              }}
              className="absolute right-2 top-1/2 z-10 flex h-12 w-12 -translate-y-1/2 items-center justify-center rounded-full bg-white/10 text-white transition hover:bg-white/20 md:right-6"
              aria-label="Next"
            >
              <ChevronRight className="h-6 w-6" />
            </button>
          </>
        ) : null}

        <div
          className="flex h-full w-full items-center justify-center px-4 py-2"
          onClick={(e) => {
            e.stopPropagation();
            if (caption) setIsCaptionVisible((visible) => !visible);
          }}
          onTouchStart={(e) => {
            touchStartX.current = e.touches[0].clientX;
          }}
          onTouchEnd={(e) => {
            if (touchStartX.current == null) return;
            const dx = e.changedTouches[0].clientX - touchStartX.current;
            if (Math.abs(dx) > 40) {
              if (dx > 0) goPrev();
              else goNext();
            }
            touchStartX.current = null;
          }}
        >
          <img
            src={current.url}
            alt={current.filename}
            className="max-h-full max-w-full select-none object-contain"
            draggable={false}
          />
        </div>

        {showCaption ? (
          <div
            className="absolute inset-x-0 bottom-0 z-20 bg-gradient-to-t from-black via-black/90 to-black/70 px-4 py-4 text-sm leading-snug text-white sm:px-6"
            onClick={(e) => e.stopPropagation()}
          >
            <div className="message-stream-copyable-text mx-auto max-h-[32vh] max-w-[920px] overflow-y-auto whitespace-pre-wrap break-words [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
              {autolink(caption)}
            </div>
          </div>
        ) : null}
      </div>

      {/* Bottom thumbnail filmstrip */}
      {hasMany ? (
        <Filmstrip images={images} index={index} onSelect={setIndex} />
      ) : null}
    </div>,
    document.body,
  );
}
