import { action, computed, flow, observable, runInAction } from "mobx";

import type { ClientBlockerTemplate, ClientServiceTemplate } from "@devowl-wp/service-cloud-consumer";

import { BlockerCollection } from "../models/blockerCollection.js";
import { BlockerTemplate } from "../models/blockerTemplate.js";
import { CookieGroupCollection } from "../models/cookieGroupCollection.js";
import { ServiceTemplate } from "../models/serviceTemplate.js";
import { request } from "../utils/request.js";
import { locationRestCookieUnassignedGet } from "../wp-api/cookieUnassigned.get.js";
import { locationRestTemplatesBlockersGet } from "../wp-api/templatesBlocker.get.js";
import { locationRestTemplatesServicesGet } from "../wp-api/templatesServices.get.js";

import type { RootStore } from "./stores.js";
import type { CookieGroupModel } from "../models/cookieGroupModel.js";
import type {
    ParamsRouteCookieUnassignedGet,
    RequestRouteCookieUnassignedGet,
    ResponseRouteCookieUnassignedGet,
} from "../wp-api/cookieUnassigned.get.js";
import type {
    ParamsRouteTemplatesBlockersGet,
    RequestRouteTemplatesBlockersGet,
    ResponseRouteTemplatesBlockersGet,
} from "../wp-api/templatesBlocker.get.js";
import type {
    ParamsRouteTemplatesServicesGet,
    RequestRouteTemplatesServicesGet,
    ResponseRouteTemplatesServicesGet,
} from "../wp-api/templatesServices.get.js";

class CookieStore {
    @observable
    public busy = false;

    @observable
    public groups: CookieGroupCollection;

    @observable
    public unassignedCookies = new Map<number, ResponseRouteCookieUnassignedGet[0]>();

    @observable
    public blockers: BlockerCollection;

    @observable
    public templatesBlocker = new Map<string, BlockerTemplate>();

    @observable
    public busyTemplatesBlocker = false;

    @observable
    public templatesServices = new Map<string, ServiceTemplate>();

    @observable
    public busyTemplatesServices = false;

    @observable
    public fetchedAllBlockers = false;

    public readonly rootStore: RootStore;

    @computed
    public get blockersCount() {
        return this.fetchedAllBlockers ? this.blockers.entries.size : this.rootStore.optionStore.allBlockerCount;
    }

    @computed
    public get cookiesCount() {
        return Array.from(this.groups.entries.values())
            .map(({ cookiesCount }) => cookiesCount)
            .reduce((total, num) => total + num, 0);
    }

    public constructor(rootStore: RootStore) {
        this.rootStore = rootStore;
        runInAction(() => {
            this.groups = new CookieGroupCollection(this);
            this.blockers = new BlockerCollection(this);
        });
    }

    @computed
    public get essentialGroup() {
        if (this.groups.entries.size === 0) {
            return undefined;
        }

        const it = this.groups.entries.values();
        let group: CookieGroupModel;
        while ((group = it.next().value)) {
            if (group.data.meta.isEssential) {
                break;
            }
        }
        return group;
    }

    public fetchGroups: () => Promise<void> = flow(function* (this: CookieStore) {
        yield this.groups.get({
            params: {
                per_page: 100,
            },
        });
        yield this.fetchUnassignedCookies();
    });

    public fetchUnassignedCookies: () => Promise<void> = flow(function* (this: CookieStore) {
        try {
            const items: ResponseRouteCookieUnassignedGet = yield request<
                RequestRouteCookieUnassignedGet,
                ParamsRouteCookieUnassignedGet,
                ResponseRouteCookieUnassignedGet
            >({
                location: locationRestCookieUnassignedGet,
            });

            // Save unassigned service as object
            for (const item of Object.values(items)) {
                this.unassignedCookies.set(item.id, item);
            }
        } catch (e) {
            console.log(e);
            throw e;
        }
    });

    public fetchBlockers: () => Promise<void> = flow(function* (this: CookieStore) {
        yield this.blockers.get({
            request: {
                status: ["draft", "publish", "private"],
            },
            params: {
                per_page: 100, // Fetch all
                context: "edit",
            },
        });
        this.fetchedAllBlockers = true;
    });

    public fetchTemplatesBlocker: (params?: ParamsRouteTemplatesServicesGet) => Promise<void> = flow(function* (
        this: CookieStore,
        params,
    ) {
        this.busyTemplatesBlocker = true;
        try {
            const { items }: ResponseRouteTemplatesBlockersGet = yield request<
                RequestRouteTemplatesBlockersGet,
                ParamsRouteTemplatesBlockersGet,
                ResponseRouteTemplatesBlockersGet
            >({
                location: locationRestTemplatesBlockersGet,
                params,
            });

            this.templatesBlocker.clear();
            this.addBlockerTemplates(items);
        } catch (e) {
            console.log(e);
            throw e;
        } finally {
            this.busyTemplatesBlocker = false;
        }
    });

    public fetchTemplatesServices: (params?: ParamsRouteTemplatesServicesGet) => Promise<void> = flow(function* (
        this: CookieStore,
        params,
    ) {
        this.busyTemplatesServices = true;
        try {
            const { items }: ResponseRouteTemplatesServicesGet = yield request<
                RequestRouteTemplatesServicesGet,
                ParamsRouteTemplatesServicesGet,
                ResponseRouteTemplatesServicesGet
            >({
                location: locationRestTemplatesServicesGet,
                params,
            });

            // Load other languages in a multilingual environment
            if (["redownload", "invalidate"].indexOf(params?.storage) > -1) {
                const { activeLanguages, currentLanguage } = this.rootStore.optionStore.others;
                for (const activeLanguage of activeLanguages) {
                    if (activeLanguage !== currentLanguage) {
                        yield request<
                            RequestRouteTemplatesServicesGet,
                            ParamsRouteTemplatesServicesGet,
                            ResponseRouteTemplatesServicesGet
                        >({
                            location: locationRestTemplatesServicesGet,
                            params: {
                                ...params,
                                _dataLocale: activeLanguage,
                            },
                        });
                    }
                }
            }

            this.templatesServices.clear();
            this.addServiceTemplates(items);
        } catch (e) {
            console.log(e);
            throw e;
        } finally {
            this.busyTemplatesServices = false;
        }
    });

    @action
    public addBlockerTemplates(items: ClientBlockerTemplate["afterPersist"][]) {
        for (const template of items) {
            this.templatesBlocker.set(template.identifier, new BlockerTemplate(template, this));
        }
    }

    @action
    public addServiceTemplates(items: ClientServiceTemplate["afterPersist"][]) {
        for (const template of items) {
            this.templatesServices.set(template.identifier, new ServiceTemplate(template, this));
        }
    }
}

export { CookieStore };
