import {
    ButtonComponent,
    htmlToMarkdown,
    MarkdownRenderer,
    Menu,
    Modal,
} from "obsidian";
import {RssFeedItem} from "../parser/rssParser";
import RssReaderPlugin from "../main";
import Action from "../actions/Action";
import t from "../l10n/locale";
import {copy} from "obsidian-community-lib";
import {rssToMd} from "../functions";

export class ItemModal extends Modal {

    private readonly plugin: RssReaderPlugin;
    private readonly item: RssFeedItem;
    private readonly items: RssFeedItem[];

    private readonly save: boolean;

    private readButton: ButtonComponent;
    private favoriteButton: ButtonComponent;

    constructor(plugin: RssReaderPlugin, item: RssFeedItem, items: RssFeedItem[], save = true) {
        super(plugin.app);
        this.plugin = plugin;
        this.items = items;
        this.item = item;
        this.save = save;


        if (this.save) {
            this.item.read = true;

            const feedContents = this.plugin.settings.items;
            this.plugin.writeFeedContent(() => {
                return feedContents;
            });

            if (!this.plugin.settings) {
                return;
            }

            if (this.plugin.settings.hotkeys.read) {
                this.scope.register([], this.plugin.settings.hotkeys.read, () => {
                    this.markAsRead();
                });
            }

            if (this.plugin.settings.hotkeys.favorite) {
                this.scope.register([], this.plugin.settings.hotkeys.favorite, () => {
                    this.markAsFavorite();
                });
            }

            if (this.plugin.settings.hotkeys.tags) {
                this.scope.register([], this.plugin.settings.hotkeys.tags, () => {
                    Action.TAGS.processor(this.plugin, this.item);
                });
            }
        }

        if (this.plugin.settings.hotkeys.create) {
            this.scope.register([], this.plugin.settings.hotkeys.create, () => {
                Action.CREATE_NOTE.processor(this.plugin, this.item);
            });
        }

        if (this.plugin.settings.hotkeys.paste) {
            this.scope.register([], this.plugin.settings.hotkeys.paste, () => {
                Action.PASTE.processor(this.plugin, this.item);
            });
        }

        if (this.plugin.settings.hotkeys.copy) {
            this.scope.register([], this.plugin.settings.hotkeys.copy, () => {
                Action.COPY.processor(this.plugin, this.item);
            });
        }

        if (this.plugin.settings.hotkeys.open) {
            this.scope.register([], this.plugin.settings.hotkeys.open, () => {
                Action.OPEN.processor(this.plugin, this.item);
            });
        }

        if (this.plugin.settings.hotkeys.next) {
            this.scope.register([], this.plugin.settings.hotkeys.next, () => {
                this.next();
            });
        }
        if (this.plugin.settings.hotkeys.previous) {
            this.scope.register([], this.plugin.settings.hotkeys.previous, () => {
                this.previous();
            });
        }


        //@ts-ignore
        if (this.app.plugins.plugins["obsidian-tts"] && this.plugin.settings.hotkeys.tts) {
            this.scope.register([], this.plugin.settings.hotkeys.tts, () => {
                //@ts-ignore
                const tts = this.app.plugins.plugins["obsidian-tts"].ttsService;
                if (tts.isSpeaking()) {
                    if (tts.isPaused()) {
                        tts.resume();
                    } else {
                        tts.pause();
                    }
                    return;
                }
                const content = htmlToMarkdown(this.item.content);
                tts.say(this.item.title, content, this.item.language);
            });
        }
    }

    previous(): void {
        let index = this.items.findIndex((item) => {
            return item === this.item;
        });
        index++;
        const item = this.items[index];
        if (item !== undefined) {
            this.close();
            new ItemModal(this.plugin, item, this.items, this.save).open();
        }
    }

    next(): void {
        let index = this.items.findIndex((item) => {
            return item === this.item;
        });
        index--;
        const item = this.items[index];
        if (item !== undefined) {
            this.close();
            new ItemModal(this.plugin, item, this.items, this.save).open();
        }
    }

    async markAsFavorite(): Promise<void> {
        await Action.FAVORITE.processor(this.plugin, this.item);
        this.favoriteButton.setIcon((this.item.favorite) ? 'star-glyph' : 'star');
        this.favoriteButton.setTooltip((this.item.favorite) ? t("remove_from_favorites") : t("mark_as_favorite"));
    }

    async markAsRead(): Promise<void> {
        await Action.READ.processor(this.plugin, this.item);
        this.readButton.setIcon((this.item.read) ? 'eye-off' : 'eye');
        this.readButton.setTooltip((this.item.read) ? t("mark_as_unread") : t("mark_as_unread"));
    }

    async display(): Promise<void> {
        this.modalEl.addClass("rss-modal");
        const {contentEl} = this;
        contentEl.empty();

        //don't add any scrolling to modal content
        contentEl.style.height = "100%";
        contentEl.style.overflowY = "hidden";

        const topButtons = contentEl.createDiv('topButtons');

        let actions = Array.of(Action.CREATE_NOTE, Action.PASTE, Action.COPY, Action.OPEN);

        if (this.save) {
            this.readButton = new ButtonComponent(topButtons)
                .setIcon(this.item.read ? 'eye-off' : 'eye')
                .setTooltip(this.item.read ? t("mark_as_unread") : t("mark_as_read"))
                .onClick(async () => {
                    await this.markAsRead();
                });
            this.readButton.buttonEl.setAttribute("tabindex", "-1");
            this.readButton.buttonEl.addClass("rss-button");

            this.favoriteButton = new ButtonComponent(topButtons)
                .setIcon(this.item.favorite ? 'star-glyph' : 'star')
                .setTooltip(this.item.favorite ? t("remove_from_favorites") : t("mark_as_favorite"))
                .onClick(async () => {
                    await this.markAsFavorite();
                });
            this.favoriteButton.buttonEl.setAttribute("tabindex", "-1");
            this.favoriteButton.buttonEl.addClass("rss-button");

            actions = Array.of(Action.TAGS, ...actions);
        }


        actions.forEach((action) => {
            const button = new ButtonComponent(topButtons)
                .setIcon(action.icon)
                .setTooltip(action.name)
                .onClick(async () => {
                    await action.processor(this.plugin, this.item);
                });
            button.buttonEl.setAttribute("tabindex", "-1");
            button.buttonEl.addClass("rss-button");
        });
        //@ts-ignore
        if (this.app.plugins.plugins["obsidian-tts"]) {
            const ttsButton = new ButtonComponent(topButtons)
                .setIcon("headphones")
                .setTooltip(t("read_article_tts"))
                .onClick(async () => {
                    const content = htmlToMarkdown(this.item.content);
                    //@ts-ignore
                    await this.app.plugins.plugins["obsidian-tts"].ttsService.say(this.item.title, content, this.item.language);
                });
            ttsButton.buttonEl.addClass("rss-button");
        }

        const prevButton = new ButtonComponent(topButtons)
            .setIcon("left-arrow-with-tail")
            .setTooltip(t("previous"))
            .onClick(() => {
                this.previous();
            });
        prevButton.buttonEl.addClass("rss-button");

        const nextButton = new ButtonComponent(topButtons)
            .setIcon("right-arrow-with-tail")
            .setTooltip(t("next"))
            .onClick(() => {
                this.next();
            });
        nextButton.buttonEl.addClass("rss-button");

        const title = contentEl.createEl('h1', 'rss-title');
        title.addClass("rss-selectable");
        title.setText(this.item.title);

        const subtitle = contentEl.createEl("h3", "rss-subtitle");
        subtitle.addClass("rss-selectable");
        if (this.item.creator) {
            subtitle.appendText(this.item.creator);
        }
        if (this.item.pubDate) {
            subtitle.appendText(" - " + window.moment(this.item.pubDate).format(this.plugin.settings.dateFormat));
        }
        const tagEl = contentEl.createSpan("tags");
        this.item.tags.forEach((tag) => {
            const tagA = tagEl.createEl("a");
            tagA.setText(tag);
            tagA.addClass("tag", "rss-tag");
        });

        const content = contentEl.createDiv('rss-content');
        content.addClass("rss-scrollable-content", "rss-selectable");

        if (this.item.enclosure && this.plugin.settings.displayMedia) {
            if (this.item.enclosureType.toLowerCase().contains("audio")) {
                const audio = content.createEl("audio", {attr: {controls: "controls"}});
                audio.createEl("source", {attr: {src: this.item.enclosure, type: this.item.enclosureType}});
            }
            if (this.item.enclosureType.toLowerCase().contains("video")) {
                const video = content.createEl("video", {attr: {controls: "controls", width: "100%", height: "100%"}});
                video.createEl("source", {attr: {src: this.item.enclosure, type: this.item.enclosureType}});
            }

            //embedded yt player
            if (this.item.enclosure && this.item.id.startsWith("yt:")) {
                content.createEl("iframe", {
                    attr: {
                        type: "text/html",
                        src: "https://www.youtube.com/embed/" + this.item.enclosure,
                        width: "100%",
                        height: "100%",
                        allowFullscreen: "true"
                    }
                });
            }
        }

        if (this.item.content) {
            //prepend empty yaml to fix rendering errors
            const markdown = "---\n---" + rssToMd(this.plugin, this.item.content);

            await MarkdownRenderer.renderMarkdown(markdown, content, "", this.plugin);

            this.item.highlights.forEach(highlight => {
                if (content.innerHTML.includes(highlight)) {
                    const newNode = contentEl.createEl("mark");
                    newNode.innerHTML = highlight;
                    content.innerHTML = content.innerHTML.replace(highlight, newNode.outerHTML);
                    newNode.remove();
                } else {
                    console.log("Highlight not included");
                    console.log(highlight);
                }
            });

            content.addEventListener('contextmenu', (event) => {
                event.preventDefault();

                const selection = document.getSelection();
                const range = selection.getRangeAt(0);

                const div = contentEl.createDiv();
                const htmlContent = range.cloneContents();
                const html = htmlContent.cloneNode(true);
                div.appendChild(html);
                const selected = div.innerHTML;
                div.remove();

                const menu = new Menu(this.app);

                let previousHighlight: HTMLElement;
                if (this.item.highlights.includes(range.startContainer.parentElement.innerHTML)) {
                    previousHighlight = range.startContainer.parentElement;
                }
                if (this.item.highlights.includes(range.startContainer.parentElement.parentElement.innerHTML)) {
                    previousHighlight = range.startContainer.parentElement.parentElement;
                }

                if(previousHighlight) {
                    menu.addItem(item => {
                        item
                            .setIcon("highlight-glyph")
                            .setTitle(t("highlight_remove"))
                            .onClick(async () => {
                                const replacement = contentEl.createSpan();
                                replacement.innerHTML = previousHighlight.innerHTML;
                                previousHighlight.replaceWith(replacement);
                                this.item.highlights.remove(previousHighlight.innerHTML);

                                const feedContents = this.plugin.settings.items;
                                await this.plugin.writeFeedContent(() => {
                                    return feedContents;
                                });
                            });
                    });
                }else if(!this.item.highlights.includes(selected) && selected.length > 0) {
                    menu.addItem(item => {
                        item
                            .setIcon("highlight-glyph")
                            .setTitle(t("highlight"))
                            .onClick(async () => {
                                const newNode = contentEl.createEl("mark");
                                newNode.innerHTML = selected;
                                range.deleteContents();
                                range.insertNode(newNode);
                                this.item.highlights.push(selected);

                                const feedContents = this.plugin.settings.items;
                                await this.plugin.writeFeedContent(() => {
                                    return feedContents;
                                });

                                //cleaning up twice to remove nested elements
                                this.removeDanglingElements(contentEl);
                                this.removeDanglingElements(contentEl);
                            });
                    });
                }

                if(selected.length > 0) {
                    menu
                        .addItem(item => {
                            item
                                .setIcon("documents")
                                .setTitle(t("copy_to_clipboard"))
                                .onClick(async () => {
                                    await copy(selection.toString());
                                });
                        });
                    //@ts-ignore
                    if (this.app.plugins.plugins["obsidian-tts"]) {
                        menu.addItem(item => {
                            item
                                .setIcon("headphones")
                                .setTitle(t("read_article_tts"))
                                .onClick(() => {
                                    //@ts-ignore
                                    const tts = this.app.plugins.plugins["obsidian-tts"].ttsService;
                                    tts.say("", selection.toString());
                                });
                        });
                    }
                }

                menu.showAtMouseEvent(event);
            });
        }
    }

    async onClose(): Promise<void> {
        const {contentEl} = this;
        contentEl.empty();

        const feedContents = this.plugin.settings.items;
        await this.plugin.writeFeedContent(() => {
            return feedContents;
        });
    }

    async onOpen(): Promise<void> {
        await this.display();
    }

    removeDanglingElements(el: HTMLElement) : void {
        //remove wallabag.xml dangling elements
        const lists = el.querySelectorAll('li, a, div, p, span');
        for (let i = 0; i < lists.length; i++) {
            const listEL = lists.item(i);
            if(listEL.innerHTML === '') {
                listEL.remove();
            }
        }
    }

}