import {defineStore} from 'pinia'
import {config} from '@/config'
import logger from "@/logger.js"
import performRequest from '@/backend.js'

export const usePremiseEditStore = defineStore('premiseEdit', {
        state() {
            return {
                premisses: null,
                selectedIds: [],
                sortedBy: 'id',
                order: new Map([['id', 'desc'], ['material', 'desc'], ['supplier', 'desc']]),

                /**
                 *  set to true while the store is loading the premises.
                 */
                loading: false,

                /**
                 * set to true while the store sets the selected/deselected field in the premises.
                 */
                selectedLoading: false,

                throwsException: true,
            }
        },
        getters: {

            /**
             * Returns the premises.
             * @param state
             * @returns {null|*[]|*}
             */
            getPremisses(state) {
                if (state.loading) {
                    if (state.throwsException)
                        throw new Error("Premises are accessed while still loading.");

                    return null;
                }

                return state.premisses;
            },

            /**
             * Returns the premise with the given id.
             * @param state
             * @returns {function(*): *}
             */
            getById(state) {
                return function (id) {
                    if (state.loading) {
                        if (state.throwsException)
                            throw new Error("Premises are accessed while still loading.");

                        return null;
                    }

                    if ((id ?? null) === null) {
                        return null;
                    }

                    return state.premisses.find(p => p.id === id);
                };
            },

            /**
             * Returns all premises that are selected.
             * @param state
             * @returns {T[]}
             */
            getSelectedPremiseIds(state) {
                if (state.loading || state.selectedLoading) {
                    if (state.throwsException)
                        throw new Error("Premises are accessed while still loading.");

                    return null;
                }

                return state.selectedIds;
            },

            /**
             * Getters for controlling frontend views
             * ======================================
             */

            /**
             * Returns true if no premises are loaded. The frontend can show a message that no premises are found.
             * @param state
             * @returns {*|boolean}
             */
            showEmpty(state) {
                return (!state.loading && (((state.premisses ?? null) === null) || state.premisses.length === 0))
            },

            /**
             * Returns true if the premises are loaded and not empty. The frontend can show the loaded premisses.
             * @param state
             * @returns {boolean}
             */
            showData(state) {
                return (!state.loading && !((state.premisses ?? null) === null) && state.premisses.length !== 0)
            },

            /**
             * Returns true if the premises are loaded and not empty. The frontend can show a the loaded premisses.
             * @param state
             * @returns {boolean}
             */
            isLoading(state) {
                return (state.loading);
            },

            showProcessingModal(state) {
                return state.processDestinationMassEdit;
            },

            /**
             * Getters for controlling getters
             * ======================================
             */

            allChecked(state) {
                if (state.premisses.length > state.selectedIds.length)
                    return false;

                for (const premise of state.premisses) {
                    if (!state.selectedIds.includes(premise.id))
                        return false;
                }

                return state.premisses.length !== 0;
            },
            someChecked(state) {
                for (const premise of state.premisses) {
                    if (state.selectedIds.includes(premise.id))
                        return true;
                }

                return false;
            },
            isChecked(state) {
                return (id) => {
                    return state.selectedIds.includes(id);
                }
            },
            activeSort(state) {
                return state.sortedBy;
            },
            directionSort(state) {
                return (sort) => {
                    return state.order.get(sort);
                }
            }

        },
        actions: {

            sort(type) {


                this.loading = true;

                const direction = (type !== this.sortedBy) ? this.order.get(type) : (this.order.get(type) === 'asc' ? 'desc' : 'asc');

                const temp = this.premisses.slice();
                temp.sort((a, b) => {
                    if (type === 'material')
                        return direction === 'asc' ?
                            a.material.part_number.localeCompare(b.material.part_number) :
                            b.material.part_number.localeCompare(a.material.part_number);
                    else if (type === 'supplier')
                        return direction === 'asc' ?
                            a.supplier.name.localeCompare(b.supplier.name) :
                            b.supplier.name.localeCompare(a.supplier.name);
                    else return a.id - b.id;
                });


                this.premisses = temp;
                this.sortedBy = type;
                this.order.set(type, direction);


                this.loading = false;
            },
            async startCalculation() {

                const body = this.premisses.map(p => p.id);
                const url = `${config.backendUrl}/calculation/start/`;
                let error = null;

                await performRequest(this, 'PUT', url, body, false, ['Premiss validation error', 'Internal Server Error']).catch(e => {
                    logger.log("startCalculation exception", e.errorObj);
                    error = e.errorObj;
                })

                return error;
            },

            /**
             * Save
             */

            async batchUpdate(type, ids, data) {
                switch (type) {
                    case 'price':
                        this.batchUpdatePrice(ids, data);
                        break;
                    case 'material':
                        this.batchUpdateMaterial(ids, data);
                        break;
                    case 'packaging':
                        this.batchUpdatePackaging(ids, data);
                        break;
                }

            },
            async batchUpdatePrice(ids, priceData) {

                const updatedPremises = this.premisses.map(p => {
                    if (ids.includes(p.id)) {
                        return {
                            ...p,
                            ...(priceData.price !== null && {material_cost: priceData.price}),
                            ...(priceData.overSeaShare !== null && {oversea_share: priceData.overSeaShare}),
                            ...(priceData.includeFcaFee !== null && {is_fca_enabled: priceData.includeFcaFee})
                        };
                    }
                    return p;
                });
                this.premisses = updatedPremises;

                return await this.savePrice(ids, priceData);
            },
            async batchUpdateMaterial(ids, materialData) {
                const updatedPremises = this.premisses.map(p => {
                    if (ids.includes(p.id)) {
                        return {
                            ...p,
                            material: {
                                ...p.material,
                            },
                            ...(materialData.hsCode !== null && {hs_code: materialData.hsCode}),
                            ...((materialData.tariffRate !== null && {tariff_rate: materialData.tariffRate}))
                        };
                    }
                    return p;
                });
                this.premisses = updatedPremises;

                return await this.saveMaterial(ids, materialData);
            },
            async batchUpdatePackaging(ids, packagingData) {

                const updatedPremises = this.premisses.map(p => {
                    if (ids.includes(p.id)) {
                        return {
                            ...p,
                            handling_unit: {
                                ...p.handling_unit,
                                ...(packagingData.weight !== null && {weight: packagingData.weight}),
                                ...(packagingData.width !== null && {width: packagingData.width}),
                                ...(packagingData.length !== null && {length: packagingData.length}),
                                ...(packagingData.height !== null && {height: packagingData.height}),
                                ...(packagingData.weightUnit !== null && {weight_unit: packagingData.weightUnit}),
                                ...(packagingData.dimensionUnit !== null && {dimension_unit: packagingData.dimensionUnit}),
                                ...(packagingData.unitCount !== null && {content_unit_count: packagingData.unitCount})
                            },
                            ...(packagingData.stackable !== null && {is_stackable: packagingData.stackable}),
                            ...(packagingData.mixable !== null && {is_mixable: packagingData.mixable})
                        };
                    }
                    return p;
                });
                this.premisses = updatedPremises;

                return await this.savePackaging(ids, packagingData);
            },
            async savePrice(ids = null, priceData = null) {
                let success = true;
                const toBeUpdated = this.premisses ? (ids ? (ids.map(id => this.premisses.find(p => String(p.id) === String(id)))) : (this.selectedIds.map(id => this.premisses.find(p => String(p.id) === String(id))))) : null;



                if (!toBeUpdated?.length) return;

                const body = {
                    premise_ids: toBeUpdated.map(p => p.id),
                    material_cost: priceData === null ? toBeUpdated[0].material_cost : priceData.price,
                    oversea_share: priceData === null ? toBeUpdated[0].oversea_share : priceData.overSeaShare,
                    is_fca_enabled: priceData === null ? toBeUpdated[0].is_fca_enabled : priceData.includeFcaFee
                };

                await performRequest(this, 'POST', `${config.backendUrl}/calculation/price/`, body, false).catch(e => {
                    success = false;
                })

                return success;
            },
            async savePackaging(ids = null, packagingData = null) {
                let success = true;

                const toBeUpdated = this.premisses ? (ids ? (ids.map(id => this.premisses.find(p => String(p.id) === String(id)))) : (this.premisses.filter(p => this.selectedIds.includes(p.id)))) : null;

                if (!toBeUpdated?.length) return;

                const body = {
                    premise_ids: toBeUpdated.map(p => p.id),

                    handling_unit: {
                        weight: packagingData === null ? toBeUpdated[0].handling_unit.weight : packagingData.weight,
                        weight_unit: packagingData === null ? toBeUpdated[0].handling_unit.weight_unit : packagingData.weightUnit,

                        length: packagingData === null ? toBeUpdated[0].handling_unit.length : packagingData.length,
                        width: packagingData === null ? toBeUpdated[0].handling_unit.width : packagingData.width,
                        height: packagingData === null ? toBeUpdated[0].handling_unit.height : packagingData.height,
                        dimension_unit: packagingData === null ? toBeUpdated[0].handling_unit.dimension_unit : packagingData.dimensionUnit,

                        content_unit_count: packagingData === null ? toBeUpdated[0].handling_unit.content_unit_count : packagingData.unitCount,
                    },

                    is_mixable: packagingData === null ? toBeUpdated[0].is_mixable : packagingData.mixable,
                    is_stackable: packagingData === null ? toBeUpdated[0].is_stackable : packagingData.stackable,

                };

                await performRequest(this, 'POST', `${config.backendUrl}/calculation/packaging/`, body, false).catch(() => {
                    success = false;
                })

                return success;

            },
            async saveMaterial(ids = null, materialData = null) {
                let success = true;
                const toBeUpdated = this.premisses ? (ids ? (ids.map(id => this.premisses.find(p => String(p.id) === String(id)))) : (this.premisses.filter(p => this.selectedIds.includes(p.id)))) : null;


                if (!toBeUpdated?.length) return;

                const body = {
                    premise_ids: toBeUpdated.map(p => p.id),

                    hs_code: materialData === null ? toBeUpdated[0].hs_code : materialData.hsCode,
                    tariff_rate: (materialData === null ? toBeUpdated[0].tariff_rate : materialData.tariffRate),
                };

                await performRequest(this, 'POST', `${config.backendUrl}/calculation/material/`, body, false).catch(() => {
                    success = false;
                })


                return success;

            },

            /**
             * PREMISE stuff
             * =================
             */
            setAll(value) {
                this.selectedLoading = true;

                const temp = [];

                if (value)
                    this.premisses.forEach(p => temp.push(p.id));

                this.selectedIds = temp;

                this.selectedLoading = false;

            },
            setChecked(premiseId, checked) {
                this.selectedLoading = true;

                if (checked) {
                    if (!this.selectedIds.includes(premiseId)) {
                        this.selectedIds.push(premiseId);
                    }
                } else {
                    const idx = this.selectedIds.indexOf(premiseId);
                    if (idx !== -1)
                        this.selectedIds.splice(idx, 1);
                }

                this.selectedLoading = false;
            },
            setBy(type, action, ofId) {
                this.selectedLoading = true;
                const premise = this.premisses.find(p => p.id === ofId);

                const temp = [];

                if (action === 'append')
                    temp.push(...this.selectedIds);

                this.premisses.forEach(p => {
                    if (type === 'supplier' && p.supplier.id === premise.supplier.id) {
                        temp.push(p.id);
                    } else if (type === 'material' && p.material.id === premise.material.id) {
                        temp.push(p.id);
                    }
                });

                this.selectedIds = temp;

                this.selectedLoading = false;
            },
            async load(ids) {

                this.loading = true;
                this.premises = [];

                const params = new URLSearchParams();
                params.append('premissIds', ids.join(', '));
                const url = `${config.backendUrl}/calculation/edit/${params.size === 0 ? '' : '?'}${params.toString()}`;

                const {data: data, headers: headers} = await performRequest(this, 'GET', url, null).catch(e => {
                    this.loading = false;
                });
                this.premisses = data;

                this.selectedIds = [];

                this.loading = false;

                return this.premisses;
            },
            removePremise(id) {
                const idx = this.premisses.findIndex(p => p.id === id);
                this.premisses.splice(idx, 1);
            }
        }
    })
;