import {
    useEffect,
    useState,
    useMemo,
    type ComponentPropsWithoutRef,
    type ReactElement,
    type ReactNode,
} from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import rehypeRaw from "rehype-raw";
import rehypeSanitize from "rehype-sanitize";
import { getHighlighter } from "../utils/highlighter";

function CopyButton({ code }: { code: string }): ReactElement {
    return (
        <button
            onClick={(e) => {
                const btn = e.currentTarget;
                navigator.clipboard.writeText(code).then(() => {
                    btn.textContent = "Copied!";
                    setTimeout(() => {
                        btn.textContent = "\uf0c5";
                    }, 1500);
                });
            }}
            className="absolute top-1 right-1 px-2 py-1 text-base leading-none text-ctp-subtext0 hover:text-ctp-green transition-colors cursor-pointer select-none z-10 font-[NerdFontSymbols]"
        >
            
        </button>
    );
}

function HighlightedBlock({
    code,
    lang,
    children,
}: {
    code: string;
    lang: string;
    children: ReactNode;
}): ReactElement {
    const [html, setHtml] = useState<string | null>(null);

    useEffect(() => {
        getHighlighter().then((hl) => {
            setHtml(
                hl.codeToHtml(code, {
                    lang,
                    themes: {
                        dark: "catppuccin-macchiato",
                        light: "catppuccin-latte",
                    },
                    defaultColor: false,
                }),
            );
        });
    }, [code, lang]);

    return (
        <div className="relative border-2 border-ctp-text/50 rounded-md overflow-hidden my-6 bg-ctp-base">
            <span className="absolute top-0 left-0 px-2 py-1 text-xs font-mono text-ctp-mauve">
                {lang}
            </span>
            <CopyButton code={code} />
            {html ? (
                <div
                    className="overflow-x-auto text-sm p-4 pt-7"
                    dangerouslySetInnerHTML={{ __html: html }}
                />
            ) : (
                <pre className="overflow-x-auto text-sm p-4 pt-7 m-0">
                    {children}
                </pre>
            )}
        </div>
    );
}

const toRawUrl = (repoUrl: string): string => {
    if (!repoUrl) return repoUrl;

    if (
        repoUrl.includes("github.com") &&
        !repoUrl.includes("raw.githubusercontent.com")
    ) {
        return repoUrl
            .replace("github.com", "raw.githubusercontent.com")
            .replace("/blob/", "/");
    }

    if (repoUrl.includes("/src/branch/")) {
        return repoUrl.replace("/src/branch/", "/raw/branch/");
    }

    return repoUrl;
};

const getRepoRawBase = (repoUrl: string): string | null => {
    const rawUrl = toRawUrl(repoUrl);
    if (!rawUrl) return null;

    try {
        const url = new URL(rawUrl);
        const parts = url.pathname.split("/").filter(Boolean);

        if (url.hostname.includes("raw.githubusercontent.com")) {
            return `${url.origin}/${parts.slice(0, 3).join("/")}`;
        }

        const rawIndex = parts.indexOf("raw");
        if (rawIndex !== -1) {
            return `${url.origin}/${parts.slice(0, rawIndex + 3).join("/")}`;
        }

        return null;
    } catch {
        return null;
    }
};

const resolveImageSrc = (src: string, base: string | null): string => {
    if (!src) return src;
    if (src.startsWith("http")) return src;
    if (!base) return src;

    const clean = src.replace(/^.\//, "");
    if (src.startsWith("/")) return base + src;
    return `${base}/${clean}`;
};

interface MdProcessorProps {
    content?: string;
    repoUrl?: string;
}

export default function MdProcessor({
    content: directContent,
    repoUrl,
}: MdProcessorProps): ReactElement {
    const useRemote = !directContent && !!repoUrl;

    const [fetchState, setFetchState] = useState<{
        url: string;
        content: string;
    }>({ url: "", content: "" });

    useEffect(() => {
        if (!useRemote || !repoUrl) return;

        const fetchREADME = async (url: string): Promise<string> => {
            try {
                const rawUrl = toRawUrl(url);
                const response = await fetch(rawUrl);

                if (!response.ok) {
                    throw new Error("Failed to fetch README");
                }

                return await response.text();
            } catch (error) {
                console.error(error);
                return "Error fetching README. Make sure the URL points to a raw markdown file.";
            }
        };

        fetchREADME(repoUrl).then((content) => {
            setFetchState({ url: repoUrl, content });
        });
    }, [useRemote, repoUrl]);

    const base = useMemo(() => {
        if (directContent) return null;
        return repoUrl ? getRepoRawBase(repoUrl) : null;
    }, [directContent, repoUrl]);

    const loading = useRemote && fetchState.url !== repoUrl;
    const displayContent = directContent ?? fetchState.content;

    const markdownComponents = {
        code({
            className,
            children,
            ...props
        }: ComponentPropsWithoutRef<"code">): ReactElement {
            const isBlock =
                className ||
                (typeof children === "string" && children.includes("\n"));

            if (isBlock) {
                return (
                    <code className="text-sm" {...props}>
                        {children}
                    </code>
                );
            }

            return (
                <code
                    className="bg-ctp-mantle px-1.5 py-0.5 rounded text-sm font-mono"
                    {...props}
                >
                    {children}
                </code>
            );
        },

        pre({ children }: ComponentPropsWithoutRef<"pre">): ReactElement {
            const codeChild = Array.isArray(children) ? children[0] : children;

            let lang = "";
            let code = "";

            if (
                codeChild &&
                typeof codeChild === "object" &&
                "props" in codeChild
            ) {
                const el = codeChild as {
                    props: { className?: string; children?: string };
                };
                const cls = el.props?.className;
                const match = cls?.match(/language-(\w+)/);
                if (match) lang = match[1];
                if (typeof el.props?.children === "string") {
                    code = el.props.children;
                }
            }

            if (lang && code) {
                return (
                    <HighlightedBlock code={code} lang={lang}>
                        {children}
                    </HighlightedBlock>
                );
            }

            return (
                <div className="relative border-2 border-ctp-text/50 rounded-md overflow-hidden my-6 bg-ctp-base">
                    <CopyButton code={code} />
                    <pre className="overflow-x-auto text-sm p-4 m-0">
                        {children}
                    </pre>
                </div>
            );
        },

        h1({ children }: ComponentPropsWithoutRef<"h1">): ReactElement {
            return (
                <h1 className="text-3xl font-bold text-ctp-mauve mt-8 mb-4 border-b pb-2 border-ctp-green">
                    {children}
                </h1>
            );
        },

        h2({ children }: ComponentPropsWithoutRef<"h2">): ReactElement {
            return (
                <h2 className="text-2xl font-bold text-ctp-lavender mt-8 mb-4 border-b pb-2 border-ctp-green">
                    {children}
                </h2>
            );
        },

        h3({ children }: ComponentPropsWithoutRef<"h3">): ReactElement {
            return (
                <h3 className="text-xl font-bold text-ctp-lavender mt-5 mb-3">
                    {children}
                </h3>
            );
        },

        p({ children }: ComponentPropsWithoutRef<"p">): ReactElement {
            return <p className="mb-4 text-ctp-text">{children}</p>;
        },

        ul({ children }: ComponentPropsWithoutRef<"ul">): ReactElement {
            return <ul className="list-disc pl-6 mb-4">{children}</ul>;
        },

        ol({ children }: ComponentPropsWithoutRef<"ol">): ReactElement {
            return <ol className="list-decimal pl-6 mb-4">{children}</ol>;
        },

        li({ children }: ComponentPropsWithoutRef<"li">): ReactElement {
            return <li className="mb-1">{children}</li>;
        },

        a({ children, href }: ComponentPropsWithoutRef<"a">): ReactElement {
            return (
                <a
                    href={href}
                    target="_blank"
                    rel="noreferrer"
                    className="hover:underline text-ctp-green"
                >
                    {children}
                </a>
            );
        },

        blockquote({
            children,
        }: ComponentPropsWithoutRef<"blockquote">): ReactElement {
            return (
                <blockquote className="border-l-4 border-ctp-text pl-4 py-1 mb-4 bg-ctp-mantle rounded-r-lg">
                    {children}
                </blockquote>
            );
        },

        table({ children }: ComponentPropsWithoutRef<"table">): ReactElement {
            return (
                <div className="overflow-x-auto my-4">
                    <table className="w-full border-collapse border border-ctp-overlay0">
                        {children}
                    </table>
                </div>
            );
        },

        th({ children }: ComponentPropsWithoutRef<"th">): ReactElement {
            return (
                <th className="border border-ctp-overlay0 bg-ctp-surface0 px-4 py-2 text-left font-bold text-ctp-lavender">
                    {children}
                </th>
            );
        },

        td({ children }: ComponentPropsWithoutRef<"td">): ReactElement {
            return (
                <td className="border border-ctp-overlay0 px-4 py-2 text-ctp-text">
                    {children}
                </td>
            );
        },

        img({ src, alt }: ComponentPropsWithoutRef<"img">): ReactElement {
            const resolved = resolveImageSrc(src || "", base);

            return (
                <img
                    src={resolved}
                    alt={alt}
                    className="max-w-full h-auto rounded-md my-4 shadow-sm border-2 border-ctp-text"
                    loading="lazy"
                />
            );
        },
    };

    return (
        <div className="w-full max-w-4xl mx-auto p-4 md:p-8 bg-ctp-mantle rounded-xl shadow-sm border border-ctp-text">
            {loading ? (
                <div className="flex flex-col gap-3 animate-pulse w-full">
                    <div className="h-4 bg-ctp-overlay0 rounded-full w-3/4"></div>
                    <div className="h-4 bg-ctp-overlay0 rounded-full w-3/4"></div>
                    <div className="h-4 bg-ctp-overlay0 rounded-full w-3/4"></div>
                </div>
            ) : (
                <div className="text-ctp-text leading-relaxed prose prose-base md:prose-lg dark:prose-invert max-w-none">
                    <ReactMarkdown
                        remarkPlugins={[remarkGfm]}
                        rehypePlugins={[rehypeRaw, rehypeSanitize]}
                        components={markdownComponents}
                    >
                        {displayContent}
                    </ReactMarkdown>
                </div>
            )}
        </div>
    );
}
