import {capitalize, sendApiGet, TesoftComponent} from "../scripts/main.ts";
import {TesoftLoaderSection} from "./loader-section.ts";
import {TesoftPagination} from "./pagination.ts";
import {TesoftError} from "./error.ts";
import {TesoftTag} from "./tag.ts";
import {formatArticleDate, getBadgeText} from "./blog-article.ts";
import {TesoftBadge} from "./badge.ts";

export class TesoftBlogArticleList extends TesoftComponent {
    private readonly listItemTemplate: HTMLTemplateElement;
    private readonly loaderSection: TesoftLoaderSection;
    private readonly listDiv: HTMLDivElement;
    private readonly tagsDiv: HTMLDivElement;
    private readonly pagination: TesoftPagination;
    private readonly error: TesoftError;

    constructor() {
        super();

        const template = document.getElementById("tesoft-blog-article-list-template") as HTMLTemplateElement;
        const templateContent = template.content;

        const shadowRoot = this.attachShadow({mode: "open"});
        shadowRoot.appendChild(templateContent.cloneNode(true));

        this.listItemTemplate = document.getElementById("tesoft-blog-article-list-item-template") as HTMLTemplateElement;
        this.loaderSection = shadowRoot.querySelector<TesoftLoaderSection>("tesoft-loader-section")!;
        this.listDiv = shadowRoot.querySelector<HTMLDivElement>(".list")!;
        this.tagsDiv = shadowRoot.querySelector<HTMLDivElement>(".tags")!;
        this.pagination = shadowRoot.querySelector<TesoftPagination>("tesoft-pagination")!;
        this.error = shadowRoot.querySelector<TesoftError>("tesoft-error")!;
    }

    async load(page: number, tags: string[]) {
        try {
            this.tagsDiv.innerHTML = "";
            for (const tagName of await this.fetchTags()) {
                const tag = document.createElement("tesoft-tag") as TesoftTag;
                tag.textContent = tagName;
                this.tagsDiv.appendChild(tag);
            }
        } catch (error: any) {
            // Ignore
        }

        this.pagination.addEventListener("update-page", (e) => {
            const page = (e as CustomEvent).detail.page;
            e.stopPropagation();

            this.dispatchEvent(new CustomEvent("update-parameters", {
                bubbles: true,
                cancelable: true,
                composed: true,
                detail: {
                    page: page,
                },
            }));
        });

        await this.reload(page, tags);
    }

    async reload(page: number, tags: string[]) {
        const itemsPerPage = 20;

        for (const tag of this.tagsDiv.children as any as TesoftTag[]) {
            const tagName = tag.textContent.trim();

            tag.onclick = () => {
                const tagSet = new Set(tags);
                if (tagSet.has(tagName)) {
                    tagSet.delete(tagName);
                } else {
                    tagSet.add(tagName);
                }
                const newTags = Array.from(tagSet);

                this.dispatchEvent(new CustomEvent("update-parameters", {
                    bubbles: true,
                    cancelable: true,
                    composed: true,
                    detail: {
                        tags: newTags,
                    },
                }));
            };

            if (tags.includes(tagName)) {
                tag.selected = "";
            } else {
                tag.selected = null;
            }
        }

        this.loaderSection.reset();
        this.listDiv.innerHTML = "";
        this.pagination.classList.add("hidden");

        const parameters: Record<string, any> = {
            offset: (page - 1) * itemsPerPage,
            limit: itemsPerPage,
        };
        if (tags) {
            parameters.tags = tags.join(",");
        }

        try {
            const response = await sendApiGet("blog", parameters);
            if (response.ok) {
                const body = await response.json();

                if (body.articles.length === 0) {
                    throw "No articles found.";
                }

                for (const article of body.articles) {
                    const anchor = this.listItemTemplate.content.children[0].cloneNode(true) as HTMLAnchorElement;
                    anchor.href = `/blog?id=${article.id}`;

                    const heading = anchor.querySelector<HTMLHeadingElement>("h3")!;
                    heading.textContent = article.title;
                    const badgeText = getBadgeText(article.status);
                    if (badgeText) {
                        const badge = document.createElement("tesoft-badge") as TesoftBadge;
                        badge.textContent = badgeText;
                        badge.gold = "";
                        heading.appendChild(badge);
                    }

                    anchor.querySelector<HTMLDivElement>(".dates")!.textContent = formatArticleDate(new Date(article.date), article['mod-date'] ? new Date(article['mod-date']) : null);

                    const tags = anchor.querySelector<HTMLDivElement>(".tags")!;
                    for (const tagName of article.tags) {
                        const tag = document.createElement("tesoft-tag") as TesoftTag;
                        tag.textContent = tagName;
                        tag.selected = "";
                        tag.small = "";
                        tag.disabled = "";
                        tags.appendChild(tag);
                    }

                    this.listDiv.appendChild(anchor);
                }

                this.pagination.curPage = page;
                this.pagination.lastPage = Math.ceil(body.total / itemsPerPage);
                this.pagination.classList.remove("hidden");
            } else {
                const body = await response.json();
                throw `${capitalize(body.message)}.`;
            }
        } catch (error: any) {
            this.error.textContent = error;
            this.error.classList.remove("hidden");
            this.listDiv.classList.add("hidden");
        }

        this.loaderSection.finish();
    }

    private async fetchTags(): Promise<string[]> {
        const tags: string[] = [];

        let offset = 0;
        const limit = 50;

        while (true) {
            const response = await sendApiGet("blog/tags", {
                offset: offset,
                limit: limit,
            });
            if (!response.ok) {
                const body = await response.json();
                throw `${capitalize(body.message)}.`;
            }

            const body = await response.json();

            for (const tag of body.tags) {
                tags.push(tag);
            }

            if (body.tags.length < limit) {
                break;
            }
        }

        return tags;
    }
}

customElements.define("tesoft-blog-article-list", TesoftBlogArticleList);
