// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT
import { definePlugin } from "@expressive-code/core";
import { h } from "@expressive-code/core/hast";
import { pluginLineNumbers } from "@expressive-code/plugin-line-numbers";
import slintGrammar from "./slint.tmLanguage.json" with { type: "json" };

function sideBorder() {
    return definePlugin({
        name: "Adds side border to slint code blocks",
        baseStyles: `

        .sideBar {
            position: absolute;
            top: calc(var(--button-spacing) - 6px);
            bottom: 0;
            left: 0;
            width: 100px;
            border-left-width: 2px;
            border-left-style: solid;
            border-color: #2479f4;
            border-top-left-radius: 0.4rem;
            border-bottom-left-radius: 0.4rem;
            pointer-events: none;
        }
        `,
        hooks: {
            postprocessRenderedBlock: (context) => {
                if (
                    context.renderData.blockAst.children[1].properties
                        .dataLanguage !== "slint"
                ) {
                    return;
                }
                const side = h("div.sideBar");

                const ast = context.renderData.blockAst;
                ast.children.push(side);

                context.renderData.blockAst = ast;
            },
        },
    });
}

function remapLanguageIdentifiers(lang) {
    switch (lang) {
        case "cpp": {
            return "C++";
        }
        case "sh": {
            return "bash";
        }
        default: {
            return lang;
        }
    }
}

function languageLabel() {
    return definePlugin({
        name: "Adds language label to code blocks",
        baseStyles: `
        .language-label {
            display: flex;
            align-items: center;
            justify-content: center;
            position: absolute;
            inset-block-start: calc(var(--ec-brdWd) + var(--button-spacing));
            inset-inline-end: calc(var(--ec-brdWd) + var(--ec-uiPadInl) );
            direction: ltr;
            font-size: 0.8rem;
            color:rgb(169, 169, 169);
            opacity: 1;
            transition: opacity 0.3s;
        }
        div.expressive-code:hover .language-label,
        .expressive-code:hover .language-label {
            opacity: 0;
        }
        `,
        hooks: {
            postprocessRenderedBlock: (context) => {
                const language =
                    context.renderData.blockAst.children[1].properties
                        .dataLanguage;

                const label = h("div.language-label", {}, [
                    remapLanguageIdentifiers(language),
                ]);

                const ast = context.renderData.blockAst;
                ast.children.push(label);

                context.renderData.blockAst = ast;
            },
        },
    });
}

function hideHashLines() {
    return definePlugin({
        name: "Hide lines starting with '# ' (rustdoc-style hidden lines)",
        hooks: {
            preprocessCode: (context) => {
                if (context.codeBlock.language !== "slint") {
                    return;
                }
                const lines = context.codeBlock.getLines();
                for (let i = lines.length - 1; i >= 0; i--) {
                    if (
                        lines[i].text.startsWith("# ") ||
                        lines[i].text === "#"
                    ) {
                        context.codeBlock.deleteLine(i);
                    }
                }
            },
        },
    });
}

function workersPlaygroundButton() {
    return definePlugin({
        name: "Adds 'Run in SlintPad' button to slint codeblocks",
        baseStyles: `
        .run {
            display: flex;
            align-items: center;
            justify-content: center;
            position: absolute;
            inset-block-start: calc(var(--ec-brdWd) + var(--button-spacing));
            inset-inline-end: calc(var(--ec-brdWd) + var(--ec-uiPadInl) * 3);
            direction: ltr;
            unicode-bidi: isolate;

            background-color: color-mix(in srgb, var(--sl-color-accent) 50%, transparent);
            color: var(--sl-color-white);
            text-decoration: none;
            width: 2rem;
            height: 2rem;
            border-radius: 50%;
            opacity: 0;
            font-size: 0;
            transition: opacity 0.3s, background-color 0.3s;

            &:hover {
                background-color: color-mix(in srgb, var(--sl-color-accent) 90%, transparent);
            }

            &::before {
                content: '';
                display: inline-block;
                margin-left: 0.25rem;
                border-style: solid;
                border-width: 0.5rem 0 0.5rem 0.75rem;
                border-color: transparent transparent transparent white;
            }
        }
        div.expressive-code:hover .run,
            .expressive-code:hover .run {
                opacity: 1;
            }
        `,
        hooks: {
            postprocessRenderedBlock: (context) => {
                if (!context.codeBlock.meta.includes("playground")) {
                    return;
                }

                const content = context.codeBlock.code;
                const url = `https://slintpad.com?snippet=${encodeURIComponent(content)}`;

                const runButton = h(
                    "a.run",
                    {
                        href: url,
                        target: "__blank",
                        title: "Open in SlintPad",
                    },
                    [],
                );

                const ast = context.renderData.blockAst;
                ast.children.push(runButton);

                context.renderData.blockAst = ast;
            },
        },
    });
}

/** @returns {import("@expressive-code/core").ExpressiveCodeConfig} */
export default function starlightExpressiveCode() {
    return {
        plugins: [
            hideHashLines(),
            workersPlaygroundButton(),
            sideBorder(),
            languageLabel(),
            pluginLineNumbers(),
        ],
        defaultProps: {
            showLineNumbers: false,
        },
        themes: ["dark-plus", "light-plus"],
        styleOverrides: {
            borderRadius: "0.4rem",
            borderColor: "var(--slint-code-background)",
            frames: { shadowColor: "transparent" },
            codeBackground: "var(--slint-code-background)",
        },
        shiki: {
            langs: [slintGrammar],
        },
        frames: {
            extractFileNameFromCode: false,
        },
    };
}
