import { defineStore } from 'pinia'
import { startsWithUppercase } from '@/composables/helpers'
import { useUserStore } from '@/stores/user'
import { useNotifyStore } from '@/stores/notify'
import twofaccountService from '@/services/twofaccountService'
import { saveAs } from 'file-saver'

export const useTwofaccounts = defineStore({
    id: 'twofaccounts',

    state: () => {
        return {
            items: [],
            selectedIds: [],
            filter: '',
            backendWasNewer: false,
            fetchedOn: null,
        }
    },

    getters: {
        filtered(state) {
            const user = useUserStore()

            return state.items.filter(
                item => {
                    if (parseInt(user.preferences.activeGroup) > 0 ) {
                        return ((item.service ? item.service.toLowerCase().includes(state.filter.toLowerCase()) : false) ||
                            item.account.toLowerCase().includes(state.filter.toLowerCase())) &&
                            (item.group_id == parseInt(user.preferences.activeGroup))
                    }
                    else {
                        return ((item.service ? item.service.toLowerCase().includes(state.filter.toLowerCase()) : false) ||
                            item.account.toLowerCase().includes(state.filter.toLowerCase()))
                    }
                }
            )
        },

        /**
         * Lists unique periods used by twofaccounts in the collection
         * ex: The items collection has 3 accounts with a period of 30s and 5 accounts with a period of 40s
         *     => The method will return [30, 40]
         */
        periods(state) {
            return state.items.filter(acc => acc.otp_type == 'totp').map(function(item) {
                return { period: item.period, generated_at: item.otp?.generated_at }
            }).filter((value, index, self) => index === self.findIndex((t) => (
                t.period === value.period
            ))).sort()
        },

        orderedIds(state) {
            return state.items.map(a => a.id)
        },

        isEmpty(state) {
            return state.items.length == 0
        },

        count(state) {
            return state.items.length
        },

        filteredCount(state) {
            return state.filtered.length
        },

        selectedCount(state) {
            return state.selectedIds.length
        },

        hasNoneSelected(state) {
            return state.selectedIds.length == 0
        }
    },

    actions: {

        /**
         * Refreshes the accounts collection using the backend
         */
        async fetch(force = false) {
            // We do not want to fetch fresh data multiple times in the same 2s timespan
            const age = Math.floor(Date.now() - this.fetchedOn)
            const isOutOfAge = age > 2000

            if (isOutOfAge || force) {
                this.fetchedOn = Date.now()

                await twofaccountService.getAll(! useUserStore().preferences.getOtpOnRequest).then(response => {
                    // Defines if the store was up-to-date with the backend
                    if (force) {
                        this.backendWasNewer = response.data.length !== this.items.length
                        
                        this.items.forEach((item) => {
                            let matchingBackendItem = response.data.find(e => e.id === item.id)
                            if (matchingBackendItem == undefined) {
                                this.backendWasNewer = true
                                return;
                            }
                            for (const field in item) {
                                if (field !== 'otp' && item[field] != matchingBackendItem[field]) {
                                    this.backendWasNewer = true
                                    return;
                                }
                            }
                        })
                    }
    
                    // Updates the state
                    this.items = response.data
                })
            }
            else this.backendWasNewer = false
        },

        /**
         * Adds an account to the current selection
         */
        select(id) {
            for (var i=0 ; i < this.selectedIds.length ; i++) {
                if ( this.selectedIds[i] === id ) {
                    this.selectedIds.splice(i,1);
                    return
                }
            }
            this.selectedIds.push(id)
        },

        /**
         * Selects all accounts
         */
        selectAll() {
            this.selectedIds = this.items.map(a => a.id)
        },

        /**
         * Selects no account
         */
        selectNone() {
            this.selectedIds = []
        },

        /**
         * Deletes selected accounts
         */
        async deleteSelected() {
            if(confirm(trans('twofaccounts.confirm.delete')) && this.selectedIds.length > 0) {
                await twofaccountService.batchDelete(this.selectedIds.join())
                .then(response => {
                    let remainingItems = this.items
                    this.selectedIds.forEach(function(id) {
                        remainingItems = remainingItems.filter(a => a.id !== id)
                    })
                    this.items = remainingItems
                    this.selectNone()
                    useNotifyStore().success({ text: trans('twofaccounts.accounts_deleted') })
                })
            }
        },

        /**
         * Exports selected accounts to a json file
         */
        export() {
            twofaccountService.export(this.selectedIds.join(), {responseType: 'blob'})
            .then((response) => {
                var blob = new Blob([response.data], {type: "application/json;charset=utf-8"});
                saveAs.saveAs(blob, "2fauth_export.json");
            })
        },

        /**
         * Saves the accounts order to db
         */
        saveOrder() {
            twofaccountService.saveOrder(this.orderedIds)
        },
        
        /**
         * Sorts accounts ascending
         */
        sortAsc() {
            this.items.sort(function(a, b) {
                if (useUserStore().preferences.sortCaseSensitive) {
                    return a.service.normalize("NFD").replace(/[\u0300-\u036f]/g, "") > b.service.normalize("NFD").replace(/[\u0300-\u036f]/g, "") ? 1 : -1
                }
                
                return a.service.localeCompare(b.service, useUserStore().preferences.lang)
            });

            this.saveOrder()
        },

        /**
         * Sorts accounts descending
        */
        sortDesc() {
            this.items.sort(function(a, b) {
                if (useUserStore().preferences.sortCaseSensitive) {
                    return a.service.normalize("NFD").replace(/[\u0300-\u036f]/g, "") < b.service.normalize("NFD").replace(/[\u0300-\u036f]/g, "") ? 1 : -1
                }

                return b.service.localeCompare(a.service, useUserStore().preferences.lang)
            });

            this.saveOrder()
        },
        
        /**
         * Gets the IDs of all accounts that match the given period
         * @param {*} period 
         * @returns {Array<Number>} IDs of matching accounts
         */
        accountIdsWithPeriod(period) {
            return this.items.filter(a => a.period == period).map(item => item.id)
        },
    },
})
