import { faCopy, far, IconPrefix } from "@fortawesome/free-regular-svg-icons";
import { fas } from "@fortawesome/free-solid-svg-icons";
import { fab } from "@fortawesome/free-brands-svg-icons";
import {
    IconDefinition,
    findIconDefinition,
    icon as getFAIcon,
    library
} from "@fortawesome/fontawesome-svg-core";

import type { IconName } from "@fortawesome/fontawesome-svg-core";

/* import { RPG } from "./rpgawesome"; */
import type { AdmonitionIconDefinition, IconType } from "src/@types";
import type ObsidianAdmonition from "src/main";
import { Notice, setIcon } from "obsidian";
import { type DownloadableIconPack, DownloadableIcons } from "./packs";
import { ObsidianIconNames, ObsidianIcons } from "./obsidian";

export { type DownloadableIconPack, DownloadableIcons };

/** Load Font Awesome Library */
library.add(fas, far, fab, faCopy);

export class IconManager {
    DOWNLOADED: {
        [key in DownloadableIconPack]?: Record<string, string>;
    } = {};
    FONT_AWESOME_MAP = new Map(
        [Object.values(fas), Object.values(far), Object.values(fab)]
            .flat()
            .map((i: IconDefinition) => {
                return [
                    i.iconName,
                    {
                        name: i.iconName,
                        type: "font-awesome" as "font-awesome"
                    }
                ];
            })
    );
    constructor(public plugin: ObsidianAdmonition) {}
    async load() {
        for (const icon of this.plugin.data.icons) {
            const exists = await this.plugin.app.vault.adapter.exists(
                this.localIconPath(icon)
            );
            if (!exists) {
                await this.downloadIcon(icon);
            } else {
                this.DOWNLOADED[icon] = JSON.parse(
                    await this.plugin.app.vault.adapter.read(
                        `${this.plugin.app.plugins.getPluginFolder()}/obsidian-admonition/${icon}.json`
                    )
                );
            }
        }
        this.setIconDefinitions();
    }
    iconDefinitions: AdmonitionIconDefinition[] = [];
    setIconDefinitions() {
        const downloaded: AdmonitionIconDefinition[] = [];
        for (const pack of this.plugin.data.icons) {
            if (!(pack in this.DOWNLOADED)) continue;
            const icons = this.DOWNLOADED[pack];
            downloaded.push(
                ...Object.keys(icons).map((name) => {
                    return { type: pack, name };
                })
            );
        }
        this.iconDefinitions = [
            ...(this.plugin.data.useFontAwesome
                ? this.FONT_AWESOME_MAP.values()
                : []),
            ...ObsidianIcons.map((name) => {
                return { type: "obsidian" as IconType, name };
            }),
            ...downloaded
        ];
    }
    iconPath(pack: DownloadableIconPack) {
        return `https://raw.githubusercontent.com/valentine195/obsidian-admonition/master/icons/${pack}/icons.json`;
    }
    localIconPath(pack: DownloadableIconPack) {
        return `${this.plugin.app.plugins.getPluginFolder()}/obsidian-admonition/${pack}.json`;
    }
    async downloadIcon(pack: DownloadableIconPack) {
        try {
            const icons: Record<string, string> = await (
                await fetch(this.iconPath(pack))
            ).json();
            this.plugin.data.icons.push(pack);
            this.plugin.data.icons = [...new Set(this.plugin.data.icons)];
            await this.plugin.app.vault.adapter.write(
                this.localIconPath(pack),
                JSON.stringify(icons)
            );
            this.DOWNLOADED[pack] = icons;
            await this.plugin.saveSettings();
            this.setIconDefinitions();

            new Notice(`${DownloadableIcons[pack]} successfully downloaded.`);
        } catch (e) {
            console.error(e);
            new Notice("Could not download icon pack");
        }
    }
    async removeIcon(pack: DownloadableIconPack) {
        await this.plugin.app.vault.adapter.remove(this.localIconPath(pack));
        delete this.DOWNLOADED[pack];
        this.plugin.data.icons.remove(pack);
        this.plugin.data.icons = [...new Set(this.plugin.data.icons)];
        await this.plugin.saveSettings();
        this.setIconDefinitions();
    }
    getIconType(str: string): IconType {
        if (findIconDefinition({ iconName: str as IconName, prefix: "fas" }))
            return "font-awesome";
        if (findIconDefinition({ iconName: str as IconName, prefix: "far" }))
            return "font-awesome";
        if (findIconDefinition({ iconName: str as IconName, prefix: "fab" }))
            return "font-awesome";
        if (ObsidianIcons.includes(str as ObsidianIconNames)) {
            return "obsidian";
        }
        for (const [pack, icons] of Object.entries(this.DOWNLOADED)) {
            if (str in icons) return pack as DownloadableIconPack;
        }
    }
    getIconModuleName(icon: AdmonitionIconDefinition) {
        if (icon.type === "font-awesome") return "Font Awesome";
        if (icon.type === "obsidian") return "Obsidian Icon";
        if (icon.type === "image") return;
        if (icon.type in DownloadableIcons) return DownloadableIcons[icon.type];
    }
    getIconNode(icon: AdmonitionIconDefinition): Element {
        if (icon.type === "image") {
            const img = new Image();
            img.src = icon.name;
            return img;
        }
        if (icon.type == "obsidian") {
            const el = createDiv();
            setIcon(el, icon.name);
            return el;
        }
        if (this.DOWNLOADED[icon.type as DownloadableIconPack]?.[icon.name]) {
            const el = createDiv();
            el.innerHTML =
                this.DOWNLOADED[icon.type as DownloadableIconPack]?.[icon.name];
            return el.children[0];
        }
        for (const prefix of ["fas", "far", "fab"] as IconPrefix[]) {
            const definition = findIconDefinition({
                iconName: icon.name as IconName,
                prefix
            });
            if (definition) return getFAIcon(definition).node[0];
        }
    }
}