import DeleteOutlineOutlined from "@mui/icons-material/DeleteOutlineOutlined";
import FileUploadOutlined from "@mui/icons-material/FileUploadOutlined";
import ImageOutlined from "@mui/icons-material/ImageOutlined";
import InsertDriveFileOutlined from "@mui/icons-material/InsertDriveFileOutlined";
import CircularProgress from "@mui/material/CircularProgress";
import Tooltip from "@mui/material/Tooltip";
import { useEffect, useId, useRef, useState } from "react";
import { uploadFile, uploadImage } from "@/shared/api/upload";
import { useToast } from "@/shared/ui/ToastProvider.jsx";
import styles from "./UploadField.module.css";

const imageAccept = "image/jpeg,image/png,image/webp,.jpg,.jpeg,.png,.webp,.svga,.pag";
const pagWasmURL = "/vendor/libpag.wasm";
let pagModulePromise;

export function UploadField({
    accept,
    className,
    disabled = false,
    density = "regular",
    kind = "image",
    label,
    onChange,
    onFileSelected,
    panelClassName,
    previewClassName,
    uploadKind,
    value = "",
}) {
    const inputId = useId();
    const inputRef = useRef(null);
    const { showToast } = useToast();
    const [uploading, setUploading] = useState(false);
    const [localPreview, setLocalPreview] = useState(null);
    const isImage = kind === "image";
    const uploadMode = uploadKind || kind;
    const useImageUpload = uploadMode === "image";
    const source = localPreview?.url || value;
    const assetKind = localPreview?.assetKind || getAssetKind(value, kind);
    const displayName = localPreview?.name || getDisplayValue(value);
    const inputAccept = accept !== undefined ? accept : useImageUpload && isImage ? imageAccept : undefined;

    useEffect(() => {
        return () => {
            if (localPreview?.url) {
                URL.revokeObjectURL(localPreview.url);
            }
        };
    }, [localPreview]);

    const openPicker = () => {
        if (!disabled && !uploading) {
            inputRef.current?.click();
        }
    };

    const handleFileChange = async (event) => {
        const file = event.target.files?.[0];
        event.target.value = "";
        if (!file) {
            return;
        }

        setLocalPreview((previous) => {
            if (previous?.url) {
                URL.revokeObjectURL(previous.url);
            }
            return {
                assetKind: getAssetKind(file.name, kind, file.type),
                name: file.name,
                url: URL.createObjectURL(file),
            };
        });
        setUploading(true);
        try {
            await onFileSelected?.(file);
            const result = useImageUpload ? await uploadImage(file) : await uploadFile(file);
            onChange(result.url);
            setLocalPreview(null);
            showToast("上传成功", "success");
        } catch (err) {
            setLocalPreview(null);
            showToast(err.message || "上传失败", "error");
        } finally {
            setUploading(false);
        }
    };

    const clearValue = () => {
        setLocalPreview(null);
        onChange("");
    };

    return (
        <div
            className={[
                styles.root,
                density === "compact" ? styles.compact : "",
                disabled ? styles.disabled : "",
                className || "",
            ]
                .filter(Boolean)
                .join(" ")}
        >
            <div className={styles.header}>
                <span className={styles.label}>{label}</span>
                <span className={styles.type}>{assetKindLabel(assetKind, isImage)}</span>
            </div>
            <div className={[styles.panel, panelClassName || ""].filter(Boolean).join(" ")}>
                <button
                    aria-label={label}
                    className={[styles.preview, previewClassName || ""].filter(Boolean).join(" ")}
                    disabled={disabled || uploading}
                    type="button"
                    onClick={openPicker}
                >
                    <AssetPreview assetKind={assetKind} isImage={isImage} src={source} />
                    {uploading ? (
                        <span className={styles.overlay}>
                            <CircularProgress color="inherit" size={18} />
                        </span>
                    ) : null}
                </button>
                <div className={styles.footer}>
                    <span className={styles.name}>{displayName || "未上传"}</span>
                    <span className={styles.actions}>
                        <Tooltip arrow title={source ? "替换" : "上传"}>
                            <span>
                                <button
                                    className={styles.action}
                                    disabled={disabled || uploading}
                                    type="button"
                                    onClick={openPicker}
                                >
                                    {uploading ? (
                                        <CircularProgress color="inherit" size={16} />
                                    ) : (
                                        <FileUploadOutlined fontSize="small" />
                                    )}
                                    {source ? "替换" : "上传"}
                                </button>
                            </span>
                        </Tooltip>
                        {source ? (
                            <Tooltip arrow title="清除">
                                <span>
                                    <button
                                        className={styles.action}
                                        disabled={disabled || uploading}
                                        type="button"
                                        onClick={clearValue}
                                    >
                                        <DeleteOutlineOutlined fontSize="small" />
                                        清除
                                    </button>
                                </span>
                            </Tooltip>
                        ) : null}
                    </span>
                </div>
            </div>
            <input
                ref={inputRef}
                accept={inputAccept}
                className={styles.input}
                id={inputId}
                type="file"
                onChange={handleFileChange}
            />
        </div>
    );
}

function AssetPreview({ assetKind, isImage, src }) {
    if (!src) {
        return (
            <span className={styles.empty}>
                {isImage ? <ImageOutlined fontSize="small" /> : <InsertDriveFileOutlined fontSize="small" />}
            </span>
        );
    }
    if (assetKind === "svga") {
        return <SVGAAssetPreview src={src} />;
    }
    if (assetKind === "pag") {
        return <PAGAssetPreview src={src} />;
    }
    if (assetKind === "image" || isImage) {
        return <RasterAssetPreview src={src} />;
    }
    return (
        <span className={styles.empty}>
            <InsertDriveFileOutlined fontSize="small" />
        </span>
    );
}

function RasterAssetPreview({ src }) {
    const [failed, setFailed] = useState(false);

    useEffect(() => {
        setFailed(false);
    }, [src]);

    if (failed) {
        return (
            <span className={styles.empty}>
                <ImageOutlined fontSize="small" />
            </span>
        );
    }

    return <img alt="" className={styles.image} src={src} onError={() => setFailed(true)} />;
}

function SVGAAssetPreview({ src }) {
    const containerRef = useRef(null);
    const [failed, setFailed] = useState(false);

    useEffect(() => {
        let cancelled = false;
        const container = containerRef.current;
        let player;
        setFailed(false);

        async function load() {
            try {
                const module = await import("svgaplayerweb");
                const SVGA = module.default || module;
                if (cancelled || !container) {
                    return;
                }
                container.innerHTML = "";
                player = new SVGA.Player(container);
                player.loops = 0;
                player.clearsAfterStop = false;
                player.setContentMode("AspectFit");
                const parser = new SVGA.Parser(container);
                parser.load(
                    src,
                    (videoItem) => {
                        if (cancelled) {
                            return;
                        }
                        player.setVideoItem(videoItem);
                        player.startAnimation();
                    },
                    () => {
                        if (!cancelled) {
                            setFailed(true);
                        }
                    },
                );
            } catch {
                if (!cancelled) {
                    setFailed(true);
                }
            }
        }

        load();

        return () => {
            cancelled = true;
            if (player) {
                try {
                    player.stopAnimation(true);
                    player.clear();
                } catch {
                    // Ignore player cleanup errors from partially loaded assets.
                }
            }
            if (container) {
                container.innerHTML = "";
            }
        };
    }, [src]);

    return (
        <span className={styles.player}>
            <span ref={containerRef} className={styles.playerSurface} />
            {failed ? <span className={styles.empty}>SVGA 预览失败</span> : null}
        </span>
    );
}

function PAGAssetPreview({ src }) {
    const canvasRef = useRef(null);
    const [failed, setFailed] = useState(false);

    useEffect(() => {
        let cancelled = false;
        let pagFile;
        let pagView;
        setFailed(false);

        async function load() {
            try {
                const PAG = await getPAGModule();
                const buffer = await fetch(src).then((response) => response.arrayBuffer());
                pagFile = await PAG.PAGFile.load(buffer);
                if (cancelled || !canvasRef.current) {
                    return;
                }
                canvasRef.current.width = pagFile.width();
                canvasRef.current.height = pagFile.height();
                pagView = await PAG.PAGView.init(pagFile, canvasRef.current, { useScale: true });
                if (!pagView) {
                    throw new Error("PAGView init failed");
                }
                pagView.setRepeatCount(0);
                await pagView.play();
            } catch {
                if (!cancelled) {
                    setFailed(true);
                }
            }
        }

        load();

        return () => {
            cancelled = true;
            if (pagView) {
                pagView.destroy();
            }
            if (pagFile?.destroy) {
                pagFile.destroy();
            }
        };
    }, [src]);

    return (
        <span className={styles.player}>
            <canvas ref={canvasRef} className={styles.pagCanvas} />
            {failed ? <span className={styles.empty}>PAG 预览失败</span> : null}
        </span>
    );
}

async function getPAGModule() {
    if (!pagModulePromise) {
        pagModulePromise = import("libpag").then(({ PAGInit }) => PAGInit({ locateFile: () => pagWasmURL }));
    }
    return pagModulePromise;
}

function getDisplayValue(value) {
    if (!value) {
        return "";
    }
    const raw = String(value);
    try {
        const url = new URL(raw);
        const name = url.pathname.split("/").filter(Boolean).pop();
        return name ? decodeURIComponent(name) : raw;
    } catch {
        return raw;
    }
}

function getAssetKind(value, kind, contentType = "") {
    const extension = getExtension(value);
    if (extension === "svga") {
        return "svga";
    }
    if (extension === "pag") {
        return "pag";
    }
    if (isRasterImageExtension(extension)) {
        return "image";
    }
    const normalizedType = String(contentType || "").toLowerCase();
    if (normalizedType.includes("svga")) {
        return "svga";
    }
    if (normalizedType.includes("pag")) {
        return "pag";
    }
    if (isRasterImageContentType(normalizedType)) {
        return "image";
    }
    return kind === "image" ? "image" : "file";
}

function isRasterImageExtension(extension) {
    return ["avif", "gif", "jpg", "jpeg", "png", "webp"].includes(extension);
}

function isRasterImageContentType(contentType) {
    return ["image/avif", "image/gif", "image/jpeg", "image/png", "image/webp"].includes(contentType);
}

function getExtension(value) {
    if (!value) {
        return "";
    }
    const raw = String(value);
    const pathname = safePathname(raw);
    const name = pathname.split("/").filter(Boolean).pop() || raw;
    const cleanName = name.split("?")[0].split("#")[0];
    const dotIndex = cleanName.lastIndexOf(".");
    return dotIndex >= 0 ? cleanName.slice(dotIndex + 1).toLowerCase() : "";
}

function safePathname(value) {
    try {
        return new URL(value).pathname;
    } catch {
        return value;
    }
}

function assetKindLabel(assetKind, isImage) {
    if (assetKind === "image") {
        return "图片";
    }
    if (assetKind === "svga") {
        return "SVGA";
    }
    if (assetKind === "pag") {
        return "PAG";
    }
    return isImage ? "图片" : "文件";
}
