import { defineStore } from 'pinia';

import { useSettingsStore } from '@/stores/setting.js';
import { useUserStore } from './user.js';
import { useExchangeRatesStore } from './exchangeRates.js';

import { isNumber, isEquals } from '@/lib/common.js';
import {
    getUnixTimeBeforeUnixTime,
    getTodayFirstUnixTime,
    getTodayLastUnixTime,
    getThisWeekFirstUnixTime,
    getThisWeekLastUnixTime,
    getThisMonthFirstUnixTime,
    getThisMonthLastUnixTime,
    getThisYearFirstUnixTime,
    getThisYearLastUnixTime
} from '@/lib/datetime.js';
import services from '@/lib/services.js';
import logger from '@/lib/logger.js';

function updateTransactionDateRange(state) {
    const userStore = useUserStore();

    state.transactionDataRange.today.startTime = getTodayFirstUnixTime();
    state.transactionDataRange.today.endTime = getTodayLastUnixTime();

    state.transactionDataRange.thisWeek.startTime = getThisWeekFirstUnixTime(userStore.currentUserFirstDayOfWeek);
    state.transactionDataRange.thisWeek.endTime = getThisWeekLastUnixTime(userStore.currentUserFirstDayOfWeek);

    state.transactionDataRange.thisMonth.startTime = getThisMonthFirstUnixTime();
    state.transactionDataRange.thisMonth.endTime = getThisMonthLastUnixTime();

    state.transactionDataRange.thisYear.startTime = getThisYearFirstUnixTime();
    state.transactionDataRange.thisYear.endTime = getThisYearLastUnixTime();

    state.transactionDataRange.lastMonth.startTime = getUnixTimeBeforeUnixTime(getThisMonthFirstUnixTime(), 1, 'months');
    state.transactionDataRange.lastMonth.endTime = getUnixTimeBeforeUnixTime(getThisMonthLastUnixTime(), 1, 'months');

    state.transactionDataRange.monthBeforeLastMonth.startTime = getUnixTimeBeforeUnixTime(getThisMonthFirstUnixTime(), 2, 'months');
    state.transactionDataRange.monthBeforeLastMonth.endTime = getUnixTimeBeforeUnixTime(getThisMonthLastUnixTime(), 2, 'months');

    for (let i = 2; i <= 10; i++) {
        state.transactionDataRange[`monthBeforeLast${i}Months`].startTime = getUnixTimeBeforeUnixTime(getThisMonthFirstUnixTime(), i + 1, 'months');
        state.transactionDataRange[`monthBeforeLast${i}Months`].endTime = getUnixTimeBeforeUnixTime(getThisMonthLastUnixTime(), i + 1, 'months');
    }
}

export const useOverviewStore = defineStore('overview', {
    state: () => ({
        transactionDataRange: {
            today: {
                startTime: getTodayFirstUnixTime(),
                endTime: getTodayLastUnixTime()
            },
            thisWeek: {
                startTime: getThisWeekFirstUnixTime(useUserStore().currentUserFirstDayOfWeek),
                endTime: getThisWeekLastUnixTime(useUserStore().currentUserFirstDayOfWeek)
            },
            thisMonth: {
                startTime: getThisMonthFirstUnixTime(),
                endTime: getThisMonthLastUnixTime()
            },
            thisYear: {
                startTime: getThisYearFirstUnixTime(),
                endTime: getThisYearLastUnixTime()
            },
            lastMonth: {
                startTime: getUnixTimeBeforeUnixTime(getThisMonthFirstUnixTime(), 1, 'months'),
                endTime: getUnixTimeBeforeUnixTime(getThisMonthLastUnixTime(), 1, 'months')
            },
            monthBeforeLastMonth: {
                startTime: getUnixTimeBeforeUnixTime(getThisMonthFirstUnixTime(), 2, 'months'),
                endTime: getUnixTimeBeforeUnixTime(getThisMonthLastUnixTime(), 2, 'months')
            },
            monthBeforeLast2Months: {
                startTime: getUnixTimeBeforeUnixTime(getThisMonthFirstUnixTime(), 3, 'months'),
                endTime: getUnixTimeBeforeUnixTime(getThisMonthLastUnixTime(), 3, 'months')
            },
            monthBeforeLast3Months: {
                startTime: getUnixTimeBeforeUnixTime(getThisMonthFirstUnixTime(), 4, 'months'),
                endTime: getUnixTimeBeforeUnixTime(getThisMonthLastUnixTime(), 4, 'months')
            },
            monthBeforeLast4Months: {
                startTime: getUnixTimeBeforeUnixTime(getThisMonthFirstUnixTime(), 5, 'months'),
                endTime: getUnixTimeBeforeUnixTime(getThisMonthLastUnixTime(), 5, 'months')
            },
            monthBeforeLast5Months: {
                startTime: getUnixTimeBeforeUnixTime(getThisMonthFirstUnixTime(), 6, 'months'),
                endTime: getUnixTimeBeforeUnixTime(getThisMonthLastUnixTime(), 6, 'months')
            },
            monthBeforeLast6Months: {
                startTime: getUnixTimeBeforeUnixTime(getThisMonthFirstUnixTime(), 7, 'months'),
                endTime: getUnixTimeBeforeUnixTime(getThisMonthLastUnixTime(), 7, 'months')
            },
            monthBeforeLast7Months: {
                startTime: getUnixTimeBeforeUnixTime(getThisMonthFirstUnixTime(), 8, 'months'),
                endTime: getUnixTimeBeforeUnixTime(getThisMonthLastUnixTime(), 8, 'months')
            },
            monthBeforeLast8Months: {
                startTime: getUnixTimeBeforeUnixTime(getThisMonthFirstUnixTime(), 9, 'months'),
                endTime: getUnixTimeBeforeUnixTime(getThisMonthLastUnixTime(), 9, 'months')
            },
            monthBeforeLast9Months: {
                startTime: getUnixTimeBeforeUnixTime(getThisMonthFirstUnixTime(), 10, 'months'),
                endTime: getUnixTimeBeforeUnixTime(getThisMonthLastUnixTime(), 10, 'months')
            },
            monthBeforeLast10Months: {
                startTime: getUnixTimeBeforeUnixTime(getThisMonthFirstUnixTime(), 11, 'months'),
                endTime: getUnixTimeBeforeUnixTime(getThisMonthLastUnixTime(), 11, 'months')
            }
        },
        transactionOverviewOptions: {
            loadLast11Months: false
        },
        transactionOverviewData: {},
        transactionOverviewStateInvalid: true
    }),
    getters: {
        transactionOverview(state) {
            const userStore = useUserStore();
            const exchangeRatesStore = useExchangeRatesStore();

            const overviewData = state.transactionOverviewData;

            if (!overviewData || !overviewData.thisMonth) {
                return {
                    thisMonth: {
                        valid: false,
                        incomeAmount: 0,
                        expenseAmount: 0,
                        incompleteIncomeAmount: false,
                        incompleteExpenseAmount: false
                    }
                };
            }

            const finalOverviewData = {};
            const defaultCurrency = userStore.currentUserDefaultCurrency;

            [
                'today',
                'thisWeek',
                'thisMonth',
                'thisYear',
                'lastMonth',
                'monthBeforeLastMonth',
                'monthBeforeLast2Months',
                'monthBeforeLast3Months',
                'monthBeforeLast4Months',
                'monthBeforeLast5Months',
                'monthBeforeLast6Months',
                'monthBeforeLast7Months',
                'monthBeforeLast8Months',
                'monthBeforeLast9Months',
                'monthBeforeLast10Months'
            ].forEach(field => {
                if (!Object.prototype.hasOwnProperty.call(overviewData, field)) {
                    return;
                }

                const item = overviewData[field];

                if (!item) {
                    return;
                }

                let totalIncomeAmount = 0;
                let totalExpenseAmount = 0;
                let hasUnCalculatedTotalIncome = false;
                let hasUnCalculatedTotalExpense = false;

                if (item.amounts) {
                    for (let i = 0; i < item.amounts.length; i++) {
                        const amount = item.amounts[i];

                        if (amount.currency !== defaultCurrency) {
                            const incomeAmount = exchangeRatesStore.getExchangedAmount(amount.incomeAmount, amount.currency, defaultCurrency);
                            const expenseAmount = exchangeRatesStore.getExchangedAmount(amount.expenseAmount, amount.currency, defaultCurrency);

                            if (isNumber(incomeAmount)) {
                                totalIncomeAmount += Math.floor(incomeAmount);
                            } else {
                                hasUnCalculatedTotalIncome = true;
                            }

                            if (isNumber(expenseAmount)) {
                                totalExpenseAmount += Math.floor(expenseAmount);
                            } else {
                                hasUnCalculatedTotalExpense = true;
                            }
                        } else {
                            totalIncomeAmount += amount.incomeAmount;
                            totalExpenseAmount += amount.expenseAmount;
                        }
                    }
                }

                finalOverviewData[field] = {
                    valid: true,
                    incomeAmount: totalIncomeAmount,
                    expenseAmount: totalExpenseAmount,
                    incompleteIncomeAmount: hasUnCalculatedTotalIncome,
                    incompleteExpenseAmount: hasUnCalculatedTotalExpense,
                    amounts: item.amounts || []
                };
            });

            return finalOverviewData;
        }
    },
    actions: {
        updateTransactionOverviewInvalidState(invalidState) {
            this.transactionOverviewStateInvalid = invalidState;
        },
        resetTransactionOverview() {
            updateTransactionDateRange(this);
            this.transactionOverviewOptions.loadLast11Months = false;
            this.transactionOverviewData = {};
            this.transactionOverviewStateInvalid = true;
        },
        loadTransactionOverview({ force, loadLast11Months }) {
            const settingsStore = useSettingsStore();

            const self = this;
            let dateChanged = false;
            let rangeChanged = false;

            if (self.transactionDataRange.today.startTime !== getTodayFirstUnixTime()) {
                dateChanged = true;
                updateTransactionDateRange(self);
            }

            if (loadLast11Months && !self.transactionOverviewOptions.loadLast11Months) {
                rangeChanged = true;
            }

            if (!dateChanged && !rangeChanged && !force && !self.transactionOverviewStateInvalid) {
                return new Promise((resolve) => {
                    resolve(self.transactionOverviewData);
                });
            }

            const requestParams = {
                useTransactionTimezone: settingsStore.appSettings.timezoneUsedForStatisticsInHomePage,
                today: self.transactionDataRange.today,
                thisWeek: self.transactionDataRange.thisWeek,
                thisMonth: self.transactionDataRange.thisMonth,
                thisYear: self.transactionDataRange.thisYear
            };

            if (loadLast11Months) {
                requestParams.lastMonth = self.transactionDataRange.lastMonth;
                requestParams.monthBeforeLastMonth = self.transactionDataRange.monthBeforeLastMonth;
                requestParams.monthBeforeLast2Months = self.transactionDataRange.monthBeforeLast2Months;
                requestParams.monthBeforeLast3Months = self.transactionDataRange.monthBeforeLast3Months;
                requestParams.monthBeforeLast4Months = self.transactionDataRange.monthBeforeLast4Months;
                requestParams.monthBeforeLast5Months = self.transactionDataRange.monthBeforeLast5Months;
                requestParams.monthBeforeLast6Months = self.transactionDataRange.monthBeforeLast6Months;
                requestParams.monthBeforeLast7Months = self.transactionDataRange.monthBeforeLast7Months;
                requestParams.monthBeforeLast8Months = self.transactionDataRange.monthBeforeLast8Months;
                requestParams.monthBeforeLast9Months = self.transactionDataRange.monthBeforeLast9Months;
                requestParams.monthBeforeLast10Months = self.transactionDataRange.monthBeforeLast10Months;
            }

            return new Promise((resolve, reject) => {
                services.getTransactionAmounts(requestParams).then(response => {
                    const data = response.data;

                    if (!data || !data.success || !data.result) {
                        reject({ message: 'Unable to retrieve transaction overview' });
                        return;
                    }

                    if (self.transactionOverviewStateInvalid) {
                        self.updateTransactionOverviewInvalidState(false);
                    }

                    if (force && data.result && isEquals(self.transactionOverviewData, data.result)) {
                        reject({ message: 'Data is up to date' });
                        return;
                    }

                    self.transactionOverviewData = data.result;
                    self.transactionOverviewOptions.loadLast11Months = loadLast11Months;

                    resolve(data.result);
                }).catch(error => {
                    if (force) {
                        logger.error('failed to force load transaction overview', error);
                    } else {
                        logger.error('failed to load transaction overview', error);
                    }

                    if (error.response && error.response.data && error.response.data.errorMessage) {
                        reject({ error: error.response.data });
                    } else if (!error.processed) {
                        reject({ message: 'Unable to retrieve transaction overview' });
                    } else {
                        reject(error);
                    }
                });
            });
        }
    }
});
