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

import { BaseOptions } from "@devowl-wp/utils";

import { Consent } from "../models/consent.js";
import { Revision } from "../models/revision.js";
import { RevisionIndependent } from "../models/revisionIndependent.js";
import { request } from "../utils/request.js";
import { locationRestConsentAllDelete } from "../wp-api/consentAll.delete.js";
import { locationRestConsentAllGet } from "../wp-api/consentAll.get.js";
import { locationRestConsentRefererGet } from "../wp-api/consentReferer.get.js";
import { locationRestRevisionGet } from "../wp-api/revision.get.js";
import { locationRestRevisionIndependentGet } from "../wp-api/revisionIndependent.get.js";

import type { RootStore } from "./stores.js";
import type {
    ParamsRouteConsentAllDelete,
    RequestRouteConsentAllDelete,
    ResponseRouteConsentAllDelete,
} from "../wp-api/consentAll.delete.js";
import type {
    ParamsRouteConsentAllGet,
    RequestRouteConsentAllGet,
    ResponseRouteConsentAllGet,
} from "../wp-api/consentAll.get.js";
import type {
    ParamsRouteConsentRefererGet,
    RequestRouteConsentRefererGet,
    ResponseRouteConsentRefererGet,
} from "../wp-api/consentReferer.get.js";
import type {
    ParamsRouteRevisionGet,
    RequestRouteRevisionGet,
    ResponseRouteRevisionGet,
} from "../wp-api/revision.get.js";
import type {
    ParamsRouteRevisionIndependentGet,
    RequestRouteRevisionIndependentGet,
    ResponseRouteRevisionIndependentGet,
} from "../wp-api/revisionIndependent.get.js";

const DATE_FORMAT = "YYYY-MM-DD";

class ConsentStore extends BaseOptions {
    @observable
    public busyConsent = false;

    @observable
    public busyReferer = false;

    @observable
    public count = 0;

    @observable
    public truncatedIpsCount = 0;

    @observable
    public perPage = 50;

    @observable
    public offset = 0;

    /**
     * Current visible page in Consent tab
     */
    @observable
    public pageCollection = new Map<number, Consent>();

    @observable
    public revisions = new Map<string, Revision>();

    @observable
    public revisionsIndependent = new Map<string, RevisionIndependent>();

    @observable
    public referer: string[] = [];

    public filters: {
        page: number;
        dates: [moment.Moment, moment.Moment];
        context: string;
        ip: ParamsRouteConsentAllGet["ip"];
        uuid: ParamsRouteConsentAllGet["uuid"];
        referer: ParamsRouteConsentAllGet["referer"];
    } = observable.object(
        {
            page: 1,
            dates: [undefined, undefined],
            context: undefined,
            referer: undefined,
            ip: undefined,
            uuid: undefined,
        },
        {},
        { deep: false },
    );

    public readonly rootStore: RootStore;

    public constructor(rootStore: RootStore) {
        super();
        this.rootStore = rootStore;
        runInAction(() => {
            this.filters.context = this.rootStore.optionStore.others.context;
        });
    }

    /**
     * This action does not actually refetch the list, you need to act on your components!
     */
    @action
    public applyPage(page: ConsentStore["filters"]["page"]) {
        this.filters.page = page;
    }

    /**
     * This action does not actually refetch the stats, you need to act on your components!
     *
     * @param dates
     */
    @action
    public applyDates(dates: ConsentStore["filters"]["dates"]) {
        this.filters.dates = dates;
    }

    /**
     * This action does not actually refetch the stats, you need to act on your components!
     *
     * @param context
     */
    @action
    public applyContext(context: ConsentStore["filters"]["context"]) {
        this.filters.context = context;
    }

    /**
     * This action does not actually refetch the list, you need to act on your components!
     */
    @action
    public applyReferer(referer: ConsentStore["filters"]["referer"]) {
        this.filters.referer = referer;
    }

    /**
     * This action does not actually refetch the list, you need to act on your components!
     */
    @action
    public applyIp(ip: ConsentStore["filters"]["ip"]) {
        this.filters.ip = ip;
    }

    /**
     * This action does not actually refetch the list, you need to act on your components!
     */
    @action
    public applyUuid(uuid: ConsentStore["filters"]["uuid"]) {
        this.filters.uuid = uuid;
    }

    public fetchAll: (params?: Record<string, any>) => Promise<void> = flow(function* (this: ConsentStore, params) {
        this.busyConsent = true;
        try {
            const { page, referer, ip, uuid, context } = this.filters;
            const dates = this.filters.dates.map((m) => (m ? m.format(DATE_FORMAT) : "")) as [string, string];
            const { count, truncatedIpsCount, items }: ResponseRouteConsentAllGet = yield request<
                RequestRouteConsentAllGet,
                ParamsRouteConsentAllGet,
                ResponseRouteConsentAllGet
            >({
                location: locationRestConsentAllGet,
                params: {
                    per_page: this.perPage,
                    offset: (page - 1) * this.perPage,
                    from: dates[0],
                    to: dates[1],
                    ip,
                    uuid,
                    referer,
                    context,
                    ...(params ? params : {}),
                },
            });

            this.count = count;

            if (truncatedIpsCount) {
                this.truncatedIpsCount = truncatedIpsCount;
            }

            this.pageCollection.clear();
            for (const item of items) {
                this.pageCollection.set(item.id, new Consent(item, this));
            }
        } catch (e) {
            this.count = 0;
            this.truncatedIpsCount = 0;
            this.pageCollection.clear();
            console.log(e);
            throw e;
        } finally {
            this.busyConsent = false;
        }
    });

    public fetchRevision: (params?: ParamsRouteRevisionGet) => Promise<void> = flow(function* (
        this: ConsentStore,
        params,
    ) {
        try {
            const response: ResponseRouteRevisionGet = yield request<
                RequestRouteRevisionGet,
                ParamsRouteRevisionGet,
                ResponseRouteRevisionGet
            >({
                location: locationRestRevisionGet,
                params,
            });

            this.revisions.set(params.hash, new Revision(response, this));
        } catch (e) {
            console.log(e);
            throw e;
        }
    });

    public fetchRevisionIndependent: (params?: ParamsRouteRevisionIndependentGet) => Promise<void> = flow(function* (
        this: ConsentStore,
        params,
    ) {
        try {
            const response: ResponseRouteRevisionIndependentGet = yield request<
                RequestRouteRevisionIndependentGet,
                ParamsRouteRevisionIndependentGet,
                ResponseRouteRevisionIndependentGet
            >({
                location: locationRestRevisionIndependentGet,
                params,
            });

            this.revisionsIndependent.set(params.hash, new RevisionIndependent(response, this));
        } catch (e) {
            console.log(e);
            throw e;
        }
    });

    public fetchReferer: (params?: ParamsRouteConsentRefererGet) => Promise<void> = flow(function* (
        this: ConsentStore,
        params,
    ) {
        this.busyReferer = true;
        try {
            const response: ResponseRouteConsentRefererGet = yield request<
                RequestRouteConsentRefererGet,
                ParamsRouteConsentRefererGet,
                ResponseRouteConsentRefererGet
            >({
                location: locationRestConsentRefererGet,
                params,
            });

            this.referer = response.items;
        } catch (e) {
            console.log(e);
            throw e;
        } finally {
            this.busyReferer = false;
        }
    });

    public deleteAll: () => Promise<void> = flow(function* (this: ConsentStore) {
        this.busyConsent = true;
        try {
            yield request<RequestRouteConsentAllDelete, ParamsRouteConsentAllDelete, ResponseRouteConsentAllDelete>({
                location: locationRestConsentAllDelete,
            });

            this.applyPage(0);
            yield this.fetchAll();
        } catch (e) {
            console.log(e);
            throw e;
        } finally {
            this.busyConsent = false;
        }
    });
}

export { ConsentStore };
