import { NavItem, NavItemButton } from "@/components/layout/nav";
import { PageNavItem } from "@/components/layout/navAtom";
import { Input } from "@/components/ui/input";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { useGalleryDoc } from "@/hook/useGalleryDoc.ts";
import { cn } from "@/lib/utils";
import { EnterFullScreenIcon, ExitFullScreenIcon, ExitIcon } from "@radix-ui/react-icons";
import { useEventListener } from "usehooks-ts";
import type { Document } from "dbtype/api";
import { useCallback, useEffect, useRef, useState } from "react";

interface ComicPageProps {
    params: {
        id: string;
    };
}

function ComicViewer({
    doc,
    totalPage,
    curPage,
    onChangePage: setCurPage,
}: {
    doc: Document;
    totalPage: number;
    curPage: number;
    onChangePage: (page: number) => void;
}) {
    const [fade, setFade] = useState(false);
    const PageDown = useCallback((step: number) => setCurPage(Math.max(curPage - step, 0)), [curPage, setCurPage]);
    const PageUp = useCallback((step: number) => setCurPage(Math.min(curPage + step, totalPage - 1)), [curPage, setCurPage, totalPage]);
    const currentImageRef = useRef<HTMLImageElement>(null);

    useEffect(() => {
        const onKeyUp = (e: KeyboardEvent) => {
            const step = e.shiftKey ? 10 : 1;
            if (e.code === "ArrowLeft") {
                PageDown(step);
            } else if (e.code === "ArrowRight") {
                PageUp(step);
            }
        };
        window.addEventListener("keyup", onKeyUp);
        return () => {
            window.removeEventListener("keyup", onKeyUp);
        };
    }, [PageDown, PageUp]);

    useEffect(() => {
        if(currentImageRef.current){
            if (curPage < 0 || curPage >= totalPage) {
                return;
            }
            const img = new Image();
            img.src = `/api/doc/${doc.id}/comic/${curPage}`;
            if (img.complete) {
                currentImageRef.current.src = img.src;
                setFade(false);
                return;
            }
            setFade(true);
            const listener = () => {
                // biome-ignore lint/style/noNonNullAssertion: <explanation>
                const currentImage = currentImageRef.current!;
                currentImage.src = img.src;
                setFade(false);
            };
            img.addEventListener("load", listener);
            return () => {
                img.removeEventListener("load", listener);
                // abort loading
                img.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEAAAAALAAAAAABAAEAAAI=;';
                // TODO: use web worker to abort loading image in the future
            };
        }
    }, [curPage, doc.id, totalPage]);

    return (
        <div className="overflow-hidden w-full h-full relative">
            <div className="absolute left-0 w-1/2 h-full z-10" onMouseDown={() => PageDown(1)} />
            <img
                ref={currentImageRef}
                className={cn("max-w-full max-h-full top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 absolute",
                    fade ? "opacity-70 transition-opacity duration-300 ease-in-out" : "opacity-100"
                )}
                alt="main content"/>
            <div className="absolute right-0 w-1/2 h-full z-10" onMouseDown={() => PageUp(1)} />
        </div>
    );
}

function clip(val: number, min: number, max: number): number {
    return Math.max(min, Math.min(max, val));
}

function useFullScreen() {
    const ref = useRef<HTMLElement>(document.documentElement);
    const [isFullScreen, setIsFullScreen] = useState(false);

    const toggleFullScreen = useCallback(() => {
        if (isFullScreen) {
            document.exitFullscreen();
        } else {
            document.documentElement.requestFullscreen();
        }
    }, [isFullScreen]);

    useEventListener("fullscreenchange", () => {
        setIsFullScreen(!!document.fullscreenElement);
    }, ref);
    return { isFullScreen, toggleFullScreen };
}

export default function ComicPage({
    params
}: ComicPageProps) {
    const { data, error, isLoading } = useGalleryDoc(params.id);
    const [curPage, setCurPage] = useState(0);
    const { isFullScreen, toggleFullScreen } = useFullScreen();
    if (isLoading) {
        // TODO: Add a loading spinner
        return <div className="p-4">
            Loading...
        </div>
    }
    if (error) {
        return <div className="p-4">Error: {String(error)}</div>
    }
    if (!data) {
        return <div className="p-4">Not found</div>
    }

    if (data.content_type !== "comic") {
        return <div className="p-4">Not a comic</div>
    }
    if (!("page" in data.additional)) {
        console.error(`invalid content : page read fail : ${JSON.stringify(data.additional)}`);
        return <div className="p-4">Error. DB error. page restriction</div>
    }

    return (
        <PageNavItem items={<>
            <NavItem to={`/doc/${params.id}`} name="Back" icon={<ExitIcon />}/>
            <NavItemButton name={isFullScreen ? "Exit Fullscreen" : "Enter Fullscreen"} icon={isFullScreen ? <ExitFullScreenIcon/> : <EnterFullScreenIcon/>} onClick={()=>{
                toggleFullScreen();
            }}  />
            <Popover>
                <PopoverTrigger>
                    <span className="text-sm text-ellipsis" >{curPage + 1}/{data.additional.page as number}</span>
                </PopoverTrigger>
                <PopoverContent className="w-28">
                        <Input type="number" value={curPage + 1} onChange={(e) => 
                            setCurPage(clip(Number.parseInt(e.target.value) - 1, 
                            0, 
                            (data.additional.page as number) - 1))} />
                </PopoverContent>
            </Popover>
        </>}>
            <ComicViewer
            curPage={curPage}
            onChangePage={setCurPage}
            doc={data} 
            totalPage={data.additional.page as number} />
        </PageNavItem>
    )
}