import { proxyLazyWebpack } from "@webpack";
import { Flux, FluxDispatcher, PopoutActions, PopoutWindowStore, SnowflakeUtils } from "@webpack/common";
import DeckPopout from "./components/DeckPopout";
import { DataStore } from "@api/index";

export interface ChannelDeck {
    id: string;
    name: string;
    color?: number;
    columns: DeckColumn[];
    open: boolean;
}

export interface DeckColumn {
    channelId: string,
    width: `${number}px` | `${number}%`;
}

export const ChannelDeckStore = proxyLazyWebpack(() => {
    class ChannelDeckStore extends Flux.Store {

        public _decks = new Map<string, ChannelDeck>();

        public loaded = false;
        public pageUnloading = false;

        public windowKeyPrefix = "DISCORD_VC_CHANNELDECK_";
        public dataStoreKey = "ChannelDeck_Decks";

        public createDeck(deckState: Partial<Omit<ChannelDeck, "id">>) {
            const deck = {
                id: SnowflakeUtils.fromTimestamp(Date.now()),
                name: "",
                columns: [],
                open: false,
                ...deckState
            };
            this.setDeck(deck);
            return deck;
        }

        public getDeck(id: string) {
            return this._decks.get(id);
        }

        public setDeck(deck: ChannelDeck, noWrite?: boolean) {
            this._decks.set(deck.id, deck);
            const { id } = deck;
            const windowKey = this.windowKeyPrefix + id;
            // @ts-ignore
            if (deck?.open && !PopoutWindowStore.getWindowOpen(windowKey))
                PopoutActions.open(
                    windowKey,
                    (key) => <DeckPopout deckId={id} windowKey={key} />, {
                    defaultWidth: 1200,
                    defaultHeight: 960
                });
            // We cannot add a beforeunload event to the window here, it will be never fired as Discord has their own handler.
            // @ts-ignore
            if (!deck?.open && PopoutWindowStore.getWindowOpen(windowKey))
                this.getDeckWindow(id).close();
            if (noWrite || !this.loaded) return;
            this.writeData();
            this.emitChange();
        };

        public deleteDeck(deck: ChannelDeck) {
            // Close window
            this.setDeck({ ...deck, open: false }, true);
            this._decks.delete(deck.id);
            this.writeData();
            // @ts-ignore
            delete PopoutWindowStore.getState()[this.windowKeyPrefix + deck.id];
            // @ts-ignore
            PopoutWindowStore.persist();
            this.emitChange();
        }

        public getDecks() {
            return this._decks.values();
        }

        public getDeckWindow(id: string) {
            return PopoutWindowStore.getWindow(this.windowKeyPrefix + id);
        }

        // Workaround for Discord's beforeunload event
        public syncClosedWindows() {
            if (this.pageUnloading) return;
            this._decks.forEach((deck) => {
                // @ts-ignore
                if (deck.open && !PopoutWindowStore.getWindowOpen(this.windowKeyPrefix + deck.id))
                    this.setDeck({ ...deck, open: false });
            });
        }

        public async loadData() {
            this.loaded = true;
            const decks: Map<string, ChannelDeck> | undefined = await DataStore.get(this.dataStoreKey);
            if (decks) {
                this._decks.clear();
                decks.forEach(deck => this.setDeck(deck, true));
            }
        }

        public async unloadData() {
            // closes all decks
            this.loaded = false;
            this._decks.forEach(deck => this.setDeck({ ...deck, open: false }, true));
            this._decks.clear();
        }

        public async writeData() {
            await DataStore.set(this.dataStoreKey, this._decks);
        }
    }

    const store = new ChannelDeckStore(FluxDispatcher, {
    });

    window.addEventListener("beforeunload", () => store.pageUnloading = true);

    PopoutWindowStore.addChangeListener(() => store.syncClosedWindows());

    return store;
});;
