import { defineStore } from 'pinia';
import { useUserConfig } from 'src/composables/useUserConfig';
import { exportFile } from 'quasar';
import { useArrayData } from 'composables/useArrayData';
import useNotify from 'src/composables/useNotify.js';

import axios from 'axios';

const { notify } = useNotify();

export const useInvoiceOutGlobalStore = defineStore({
    id: 'invoiceOutGlobal',

    state: () => ({
        initialDataLoading: true,
        formInitialData: {
            companyFk: null,
            invoiceDate: null,
            maxShipped: null,
            clientId: null,
            printer: null,
        },
        addresses: [],
        minInvoicingDate: null,
        parallelism: null,
        invoicing: false,
        isInvoicing: false,
        status: null,
        addressIndex: 0,
        errors: [],
        printer: null,
        nRequests: 0,
        nPdfs: 0,
        totalPdfs: 0,
    }),
    actions: {
        async init() {
            await this.fetchAllData();
        },

        async fetchAllData() {
            try {
                const userInfo = await useUserConfig().fetch();
                const date = Date.vnNew();
                this.formInitialData.maxShipped = new Date(
                    date.getFullYear(),
                    date.getMonth(),
                    0
                )
                    .toISOString()
                    .substring(0, 10);

                await Promise.all([
                    this.fetchParallelism(),
                    this.fetchInvoiceOutConfig(userInfo.companyFk),
                ]);

                this.initialDataLoading = false;
            } catch (err) {
                console.error('Error fetching invoice out global initial data');
            }
        },

        async fetchInvoiceOutConfig(companyFk) {
            try {
                this.formInitialData.companyFk = companyFk;
                const params = { companyFk: companyFk };

                const { data } = await axios.get('InvoiceOuts/getInvoiceDate', {
                    params,
                });

                const stringDate = data.issued.substring(0, 10);
                this.minInvoicingDate = stringDate;
                this.formInitialData.invoiceDate = stringDate;

                this.minInvoicingDate = new Date(data.issued);
                this.formInitialData.invoiceDate = this.minInvoicingDate;
            } catch (err) {
                console.error('Error fetching invoice out global initial data');
                throw new Error();
            }
        },

        async fetchParallelism() {
            const filter = { fields: ['parallelism'] };
            const { data } = await axios.get('InvoiceOutConfigs/findOne', {
                filter,
            });
            this.parallelism = data.parallelism;
        },

        async makeInvoice(formData, clientsToInvoice) {
            this.invoicing = true;
            this.status = 'packageInvoicing';
            try {
                this.printer = formData.printer;
                const params = {
                    invoiceDate: new Date(formData.invoiceDate),
                    maxShipped: new Date(formData.maxShipped),
                    clientId: formData.clientId ? formData.clientId : null,
                    companyFk: formData.companyFk,
                };

                this.validateMakeInvoceParams(params, clientsToInvoice);

                if (clientsToInvoice == 'all') params.clientId = undefined;

                const { data } = await axios.post('InvoiceOuts/clientsToInvoice', params);
                this.addresses = data;

                if (!this.addresses || !this.addresses.length > 0) {
                    notify(
                        'invoiceOut.globalInvoices.errors.noTicketsToInvoice',
                        'negative'
                    );
                    throw new Error("There aren't addresses to invoice");
                }

                for (const address of this.addresses) {
                    await this.invoiceClient(address, formData);
                }
            } catch (err) {
                this.handleError(err);
            }
        },

        validateMakeInvoceParams(params, clientsToInvoice) {
            if (clientsToInvoice === 'one' && !params.clientId) {
                notify('invoiceOut.globalInvoices.errors.chooseValidClient', 'negative');
                throw new Error('Invalid client');
            }
            if (!params.invoiceDate || !params.maxShipped) {
                notify('invoiceOut.globalInvoices.errors.fillDates', 'negative');
                throw new Error('Missing dates');
            }
            if (params.invoiceDate < params.maxShipped) {
                notify(
                    'invoiceOut.globalInvoices.errors.invoiceDateLessThanMaxDate',
                    'negative'
                );
                throw new Error('Invalid date range');
            }

            const invoiceDateTime = new Date(params.invoiceDate).getTime();
            const minInvoiceDateTime = new Date(this.minInvoicingDate).getTime();

            if (this.minInvoicingDate && invoiceDateTime < minInvoiceDateTime) {
                notify(
                    'invoiceOut.globalInvoices.errors.invoiceWithFutureDate',
                    'negative'
                );
                throw new Error('Invoice date in the future');
            }

            if (!params.companyFk) {
                notify('invoiceOut.globalInvoices.errors.chooseValidCompany', 'negative');
                throw new Error('Invalid company');
            }
            if (!this.printer) {
                notify('invoiceOut.globalInvoices.errors.chooseValidPrinter', 'negative');
                throw new Error('Invalid printer');
            }
        },

        async invoiceClient(address, formData) {
            try {
                if (this.nRequests === this.parallelism || this.isInvoicing) return;

                if (this.status === 'stopping') {
                    if (this.nRequests) return;
                    this.invoicing = false;
                    this.status = 'done';
                    return;
                }

                const params = {
                    clientId: address.clientId,
                    addressId: address.id,
                    invoiceDate: new Date(formData.invoiceDate),
                    maxShipped: new Date(formData.maxShipped),
                    companyFk: formData.companyFk,
                };

                this.status = 'invoicing';
                this.invoicing = true;

                const { data } = await axios.post('InvoiceOuts/invoiceClient', params);

                if (data) await this.makePdfAndNotify(data, address);
                this.addressIndex++;
                this.isInvoicing = false;
            } catch (err) {
                if (
                    err &&
                    err.response &&
                    err.response.status >= 400 &&
                    err.response.status < 500
                ) {
                    this.invoiceClientError(address, err.response?.data?.error?.message);
                    return;
                } else {
                    this.invoicing = false;
                    this.status = 'done';
                    notify(
                        'invoiceOut.globalInvoices.errors.criticalInvoiceError',
                        'negative'
                    );
                    throw new Error('Critical invoicing error, process stopped');
                }
            }
        },

        async makePdfAndNotify(invoiceId, client) {
            try {
                this.nRequests++;
                this.totalPdfs++;
                const params = { printerFk: this.printer };
                await axios.post(`InvoiceOuts/${invoiceId}/makePdfAndNotify`, params);
                this.nPdfs++;
                this.nRequests--;
            } catch (err) {
                this.invoiceClientError(client, err.response?.data?.error?.message, true);
            }
        },

        invoiceClientError(client, message, isWarning) {
            this.errors.unshift({ client, message, isWarning });
        },

        handleError(err) {
            this.invoicing = false;
            this.status = null;
            throw err;
        },

        async getNegativeBasesCsv() {
            try {
                const arrayData = useArrayData('InvoiceOutNegative');
                const params = arrayData.store.currentFilter;

                const { data } = await axios.get('InvoiceOuts/negativeBasesCsv', {
                    params,
                });

                if (data.data && data.data.error) throw new Error();

                const status = exportFile('negativeBases.csv', data, {
                    encoding: 'windows-1252',
                    mimeType: 'text/csv;charset=windows-1252;',
                });

                if (status) {
                    notify('globals.downloadCSVSuccess', 'positive');
                }
            } catch (err) {
                notify('invoiceOut.negativeBases.errors.downloadCsvFailed', 'negative');
                throw new Error();
            }
        },

        // State mutations actions

        setStatusValue(status) {
            this.status = status;
        },
    },

    getters: {
        getNAddresses(state) {
            return state.addresses.length;
        },
        getPercentage(state) {
            if (this.getNAdresses <= 0 || !state.addressIndex) {
                return 0;
            }
            let porcentaje = (state.addressIndex / this.getNAddresses) * 100;
            return porcentaje;
        },
        getAddressNumber(state) {
            return state.addressIndex;
        },
    },
});
