import { nextTick, ref, watch } from 'vue';
import { defineStore } from 'pinia';
import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n';

import {
    checkVRChatCache,
    extractFileId,
    getAvailablePlatforms,
    getBundleDateSize,
    getPlatformInfo,
    replaceBioSymbols,
    storeAvatarImage
} from '../shared/utils';
import { avatarRequest, miscRequest } from '../api';
import { AppDebug } from '../service/appConfig';
import { database } from '../service/database';
import { useAdvancedSettingsStore } from './settings/advanced';
import { useAvatarProviderStore } from './avatarProvider';
import { useFavoriteStore } from './favorite';
import { useModalStore } from './modal';
import { useUiStore } from './ui';
import { useUserStore } from './user';
import { useVRCXUpdaterStore } from './vrcxUpdater';
import { watchState } from '../service/watchState';

import webApiService from '../service/webapi';

export const useAvatarStore = defineStore('Avatar', () => {
    const favoriteStore = useFavoriteStore();
    const avatarProviderStore = useAvatarProviderStore();
    const vrcxUpdaterStore = useVRCXUpdaterStore();
    const advancedSettingsStore = useAdvancedSettingsStore();
    const userStore = useUserStore();
    const modalStore = useModalStore();
    const uiStore = useUiStore();
    const { t } = useI18n();

    let cachedAvatarModerations = new Map();
    let cachedAvatars = new Map();
    let cachedAvatarNames = new Map();

    const avatarDialog = ref({
        visible: false,
        loading: false,
        activeTab: 'Info',
        lastActiveTab: 'Info',
        id: '',
        memo: '',
        ref: {},
        isFavorite: false,
        isBlocked: false,
        isQuestFallback: false,
        hasImposter: false,
        imposterVersion: '',
        isPC: false,
        isQuest: false,
        isIos: false,
        bundleSizes: {},
        platformInfo: {},
        galleryImages: [],
        galleryLoading: false,
        lastUpdated: '',
        inCache: false,
        cacheSize: '',
        cacheLocked: false,
        cachePath: '',
        fileAnalysis: {}
    });
    const avatarHistory = ref([]);

    watch(
        () => watchState.isLoggedIn,
        (isLoggedIn) => {
            avatarDialog.value.visible = false;
            cachedAvatars.clear();
            cachedAvatarNames.clear();
            cachedAvatarModerations.clear();
            avatarHistory.value = [];
            if (isLoggedIn) {
                getAvatarHistory();
            }
        },
        { flush: 'sync' }
    );

    /**
    / * @param {object} json
    / * @returns {object} ref
    */
    function applyAvatar(json) {
        json.name = replaceBioSymbols(json.name);
        json.description = replaceBioSymbols(json.description);
        let ref = cachedAvatars.get(json.id);
        if (typeof ref === 'undefined') {
            ref = {
                acknowledgements: '',
                authorId: '',
                authorName: '',
                created_at: '',
                description: '',
                featured: false,
                highestPrice: null,
                id: '',
                imageUrl: '',
                listingDate: null,
                lock: false,
                lowestPrice: null,
                name: '',
                pendingUpload: false,
                performance: {},
                productId: null,
                publishedListings: [],
                releaseStatus: '',
                searchable: false,
                styles: [],
                tags: [],
                thumbnailImageUrl: '',
                unityPackageUrl: '',
                unityPackageUrlObject: {},
                unityPackages: [],
                updated_at: '',
                version: 0,
                ...json
            };
            cachedAvatars.set(ref.id, ref);
        } else {
            const { unityPackages } = ref;
            Object.assign(ref, json);
            if (
                json.unityPackages?.length > 0 &&
                unityPackages.length > 0 &&
                !json.unityPackages[0].assetUrl
            ) {
                ref.unityPackages = unityPackages;
            }
        }
        for (const listing of ref.publishedListings) {
            listing.displayName = replaceBioSymbols(listing.displayName);
            listing.description = replaceBioSymbols(listing.description);
        }
        favoriteStore.applyFavorite('avatar', ref.id);
        if (favoriteStore.localAvatarFavoritesList.includes(ref.id)) {
            const avatarRef = ref;
            for (
                let i = 0;
                i < favoriteStore.localAvatarFavoriteGroups.length;
                ++i
            ) {
                const groupName = favoriteStore.localAvatarFavoriteGroups[i];
                if (!favoriteStore.localAvatarFavorites[groupName]) {
                    continue;
                }
                for (
                    let j = 0;
                    j < favoriteStore.localAvatarFavorites[groupName].length;
                    ++j
                ) {
                    const favoriteRef =
                        favoriteStore.localAvatarFavorites[groupName][j];
                    if (favoriteRef.id === avatarRef.id) {
                        favoriteStore.localAvatarFavorites[groupName][j] =
                            avatarRef;
                    }
                }
            }

            // update db cache
            database.addAvatarToCache(avatarRef);
        }
        return ref;
    }

    /**
     *
     * @param {string} avatarId
     * @returns
     */
    function showAvatarDialog(avatarId, options = {}) {
        const D = avatarDialog.value;
        uiStore.openDialog({
            type: 'avatar',
            id: avatarId,
            skipBreadcrumb: options.skipBreadcrumb
        });

        D.loading = true;
        D.id = avatarId;
        D.inCache = false;
        D.cacheSize = '';
        D.cacheLocked = false;
        D.cachePath = '';
        D.fileAnalysis = {};
        D.isQuestFallback = false;
        D.isPC = false;
        D.isQuest = false;
        D.isIos = false;
        D.hasImposter = false;
        D.imposterVersion = '';
        D.lastUpdated = '';
        D.bundleSizes = {};
        D.platformInfo = {};
        D.galleryImages = [];
        D.galleryLoading = true;
        D.isFavorite =
            favoriteStore.getCachedFavoritesByObjectId(avatarId) ||
            (userStore.isLocalUserVrcPlusSupporter &&
                favoriteStore.localAvatarFavoritesList.includes(avatarId));
        D.isBlocked = cachedAvatarModerations.has(avatarId);
        const ref2 = cachedAvatars.get(avatarId);
        if (typeof ref2 !== 'undefined') {
            D.ref = ref2;
            uiStore.setDialogCrumbLabel('avatar', D.id, D.ref?.name || D.id);
            if (
                ref2.releaseStatus !== 'public' &&
                ref2.authorId !== userStore.currentUser.id
            ) {
                D.loading = false;
                D.visible = false;
                return;
            }
        }
        D.visible = true;
        avatarRequest
            .getAvatar({ avatarId })
            .then((args) => {
                const ref = applyAvatar(args.json);
                D.ref = ref;
                uiStore.setDialogCrumbLabel(
                    'avatar',
                    D.id,
                    D.ref?.name || D.id
                );
                getAvatarGallery(avatarId);
                updateVRChatAvatarCache();
                if (/quest/.test(ref.tags)) {
                    D.isQuestFallback = true;
                }
                const { isPC, isQuest, isIos } = getAvailablePlatforms(
                    ref.unityPackages
                );
                D.isPC = isPC;
                D.isQuest = isQuest;
                D.isIos = isIos;
                D.platformInfo = getPlatformInfo(ref.unityPackages);
                for (let i = ref.unityPackages.length - 1; i > -1; i--) {
                    const unityPackage = ref.unityPackages[i];
                    if (unityPackage.variant === 'impostor') {
                        D.hasImposter = true;
                        D.imposterVersion = unityPackage.impostorizerVersion;
                        break;
                    }
                }
                if (Object.keys(D.bundleSizes).length === 0) {
                    getBundleDateSize(ref).then((bundleSizes) => {
                        D.bundleSizes = bundleSizes;
                    });
                }
            })
            .catch((err) => {
                D.visible = false;
                throw err;
            })
            .finally(() => {
                nextTick(() => (D.loading = false));
            });
    }

    /**
     *
     * @param {string} avatarId
     * @returns {Promise<string[]>}
     */
    async function getAvatarGallery(avatarId) {
        const D = avatarDialog.value;
        const args = await avatarRequest
            .getAvatarGallery(avatarId)
            .finally(() => {
                D.galleryLoading = false;
            });
        if (args.params.galleryId !== D.id) {
            return;
        }
        D.galleryImages = [];
        // wtf is this? why is order sorting only needed if it's your own avatar?
        const sortedGallery = args.json.sort((a, b) => {
            if (!a.order && !b.order) {
                return 0;
            }
            return a.order - b.order;
        });
        for (const file of sortedGallery) {
            const url = file.versions[file.versions.length - 1].file.url;
            D.galleryImages.push(url);
        }

        // for JSON tab treeData
        D.ref.gallery = args.json;
        return D.galleryImages;
    }

    /**
     *
     * @param {object} json
     * @returns {object} ref
     */
    function applyAvatarModeration(json) {
        // fix inconsistent Unix time response
        if (typeof json.created === 'number') {
            json.created = new Date(json.created).toJSON();
        }

        let ref = cachedAvatarModerations.get(json.targetAvatarId);
        if (typeof ref === 'undefined') {
            ref = {
                avatarModerationType: '',
                created: '',
                targetAvatarId: '',
                ...json
            };
            cachedAvatarModerations.set(ref.targetAvatarId, ref);
        } else {
            Object.assign(ref, json);
        }

        // update avatar dialog
        const D = avatarDialog.value;
        if (
            D.visible &&
            ref.avatarModerationType === 'block' &&
            D.id === ref.targetAvatarId
        ) {
            D.isBlocked = true;
        }

        return ref;
    }

    function updateVRChatAvatarCache() {
        const D = avatarDialog.value;
        if (D.visible) {
            D.inCache = false;
            D.cacheSize = '';
            D.cacheLocked = false;
            D.cachePath = '';
            checkVRChatCache(D.ref).then((cacheInfo) => {
                if (cacheInfo.Item1 > 0) {
                    D.inCache = true;
                    D.cacheSize = `${(cacheInfo.Item1 / 1048576).toFixed(2)} MB`;
                    D.cachePath = cacheInfo.Item3;
                }
                D.cacheLocked = cacheInfo.Item2;
            });
        }
    }

    /**
     *
     * @returns {Promise<void>}
     */
    async function getAvatarHistory() {
        const historyArray = await database.getAvatarHistory(
            userStore.currentUser.id
        );
        for (let i = 0; i < historyArray.length; i++) {
            const avatar = historyArray[i];
            if (avatar.authorId === userStore.currentUser.id) {
                continue;
            }
            applyAvatar(avatar);
        }
        avatarHistory.value = historyArray;
    }

    /**
     * @param {string} avatarId
     */
    function addAvatarToHistory(avatarId) {
        avatarRequest
            .getAvatar({ avatarId })
            .then((args) => {
                const ref = applyAvatar(args.json);

                database.addAvatarToCache(ref);
                database.addAvatarToHistory(ref.id);

                if (ref.authorId === userStore.currentUser.id) {
                    return;
                }

                const historyArray = avatarHistory.value;
                for (let i = 0; i < historyArray.length; ++i) {
                    if (historyArray[i].id === ref.id) {
                        historyArray.splice(i, 1);
                    }
                }

                avatarHistory.value.unshift(ref);
            })
            .catch((err) => {
                console.error('Failed to add avatar to history:', err);
            });
    }

    function clearAvatarHistory() {
        avatarHistory.value = [];
        database.clearAvatarHistory();
    }

    function promptClearAvatarHistory() {
        modalStore
            .confirm({
                description: t('confirm.clear_avatar_history'),
                title: 'Confirm'
            })
            .then(({ ok }) => {
                if (!ok) return;
                clearAvatarHistory();
            })
            .catch(() => {});
    }

    /**
     *
     * @param {string} imageUrl
     * @returns {Promise<object>}
     */
    async function getAvatarName(imageUrl) {
        const fileId = extractFileId(imageUrl);
        if (!fileId) {
            return {
                ownerId: '',
                avatarName: '-'
            };
        }
        if (cachedAvatarNames.has(fileId)) {
            return cachedAvatarNames.get(fileId);
        }
        try {
            const args = await miscRequest.getFile({ fileId });
            return storeAvatarImage(args, cachedAvatarNames);
        } catch (error) {
            console.error('Failed to get avatar images:', error);
            return {
                ownerId: '',
                vatarName: '-'
            };
        }
    }

    async function lookupAvatars(type, search) {
        const avatars = new Map();
        if (type === 'search') {
            try {
                const response = await webApiService.execute({
                    url: `${
                        avatarProviderStore.avatarRemoteDatabaseProvider
                    }?${type}=${encodeURIComponent(search)}&n=5000`,
                    method: 'GET',
                    headers: {
                        Referer: 'https://vrcx.app',
                        'VRCX-ID': vrcxUpdaterStore.vrcxId
                    }
                });
                const json = JSON.parse(response.data);
                if (AppDebug.debugWebRequests) {
                    console.log(json, response);
                }
                if (response.status === 200 && typeof json === 'object') {
                    json.forEach((avatar) => {
                        if (!avatars.has(avatar.Id)) {
                            const ref = {
                                authorId: '',
                                authorName: '',
                                name: '',
                                description: '',
                                id: '',
                                imageUrl: '',
                                thumbnailImageUrl: '',
                                created_at: '0001-01-01T00:00:00.0000000Z',
                                updated_at: '0001-01-01T00:00:00.0000000Z',
                                releaseStatus: 'public',
                                ...avatar
                            };
                            avatars.set(ref.id, ref);
                        }
                    });
                } else {
                    throw new Error(`Error: ${response.data}`);
                }
            } catch (err) {
                const msg = `Avatar search failed for ${search} with ${avatarProviderStore.avatarRemoteDatabaseProvider}\n${err}`;
                console.error(msg);
                toast.error(msg);
            }
        } else if (type === 'authorId') {
            const length =
                avatarProviderStore.avatarRemoteDatabaseProviderList.length;
            for (let i = 0; i < length; ++i) {
                const url =
                    avatarProviderStore.avatarRemoteDatabaseProviderList[i];
                const avatarArray = await lookupAvatarsByAuthor(url, search);
                avatarArray.forEach((avatar) => {
                    if (!avatars.has(avatar.id)) {
                        avatars.set(avatar.id, avatar);
                    }
                });
            }
        }
        return avatars;
    }

    async function lookupAvatarByImageFileId(authorId, fileId) {
        const length =
            avatarProviderStore.avatarRemoteDatabaseProviderList.length;
        for (let i = 0; i < length; ++i) {
            const url = avatarProviderStore.avatarRemoteDatabaseProviderList[i];
            const avatarArray = await lookupAvatarsByAuthor(url, authorId);
            for (const avatar of avatarArray) {
                if (extractFileId(avatar.imageUrl) === fileId) {
                    return avatar.id;
                }
            }
        }
        return null;
    }

    async function lookupAvatarsByAuthor(url, authorId) {
        const avatars = [];
        if (!url) {
            return avatars;
        }
        try {
            const response = await webApiService.execute({
                url: `${url}?authorId=${encodeURIComponent(authorId)}`,
                method: 'GET',
                headers: {
                    Referer: 'https://vrcx.app',
                    'VRCX-ID': vrcxUpdaterStore.vrcxId
                }
            });
            const json = JSON.parse(response.data);
            if (AppDebug.debugWebRequests) {
                console.log(json, response);
            }
            if (response.status === 200 && typeof json === 'object') {
                json.forEach((avatar) => {
                    const ref = {
                        authorId: '',
                        authorName: '',
                        name: '',
                        description: '',
                        id: '',
                        imageUrl: '',
                        thumbnailImageUrl: '',
                        created_at: '0001-01-01T00:00:00.0000000Z',
                        updated_at: '0001-01-01T00:00:00.0000000Z',
                        releaseStatus: 'public',
                        ...avatar
                    };
                    avatars.push(ref);
                });
            } else {
                throw new Error(`Error: ${response.data}`);
            }
        } catch (err) {
            const msg = `Avatar lookup failed for ${authorId} with ${url}\n${err}`;
            console.error(msg);
            toast.error(msg);
        }
        return avatars;
    }

    function selectAvatarWithConfirmation(id) {
        modalStore
            .confirm({
                description: t('confirm.select_avatar'),
                title: 'Confirm'
            })
            .then(({ ok }) => {
                if (!ok) return;
                selectAvatarWithoutConfirmation(id);
            })
            .catch(() => {});
    }

    async function selectAvatarWithoutConfirmation(id) {
        if (userStore.currentUser.currentAvatar === id) {
            toast.info('Avatar already selected');
            return;
        }
        return avatarRequest
            .selectAvatar({
                avatarId: id
            })
            .then(() => {
                toast.success('Avatar changed');
            });
    }

    function checkAvatarCache(fileId) {
        let avatarId = '';
        for (let ref of cachedAvatars.values()) {
            if (extractFileId(ref.imageUrl) === fileId) {
                avatarId = ref.id;
            }
        }
        return avatarId;
    }

    async function checkAvatarCacheRemote(fileId, ownerUserId) {
        if (advancedSettingsStore.avatarRemoteDatabase) {
            const avatarId = await lookupAvatarByImageFileId(
                ownerUserId,
                fileId
            );
            return avatarId;
        }
        return null;
    }

    async function showAvatarAuthorDialog(
        refUserId,
        ownerUserId,
        currentAvatarImageUrl
    ) {
        const fileId = extractFileId(currentAvatarImageUrl);
        if (!fileId) {
            toast.error('Sorry, the author is unknown');
        } else if (refUserId === userStore.currentUser.id) {
            showAvatarDialog(userStore.currentUser.currentAvatar);
        } else {
            let avatarId = checkAvatarCache(fileId);
            let avatarInfo;
            if (!avatarId) {
                avatarInfo = await getAvatarName(currentAvatarImageUrl);
                if (avatarInfo.ownerId === userStore.currentUser.id) {
                    userStore.refreshUserDialogAvatars(fileId);
                }
            }
            if (!avatarId) {
                avatarId = await checkAvatarCacheRemote(
                    fileId,
                    avatarInfo.ownerId
                );
            }
            if (!avatarId) {
                if (avatarInfo.ownerId === refUserId) {
                    toast.warning(
                        "It's personal (own) avatar or not found in avatar database"
                    );
                } else {
                    toast.warning('Avatar not found in avatar database');
                    userStore.showUserDialog(avatarInfo.ownerId);
                }
            }
            if (avatarId) {
                showAvatarDialog(avatarId);
            }
        }
    }

    function addAvatarWearTime(avatarId) {
        if (!userStore.currentUser.$previousAvatarSwapTime || !avatarId) {
            return;
        }
        const timeSpent =
            Date.now() - userStore.currentUser.$previousAvatarSwapTime;
        database.addAvatarTimeSpent(avatarId, timeSpent);
    }

    return {
        avatarDialog,
        avatarHistory,
        cachedAvatarModerations,
        cachedAvatars,
        cachedAvatarNames,

        showAvatarDialog,
        applyAvatarModeration,
        getAvatarGallery,
        updateVRChatAvatarCache,
        getAvatarHistory,
        addAvatarToHistory,
        applyAvatar,
        promptClearAvatarHistory,
        getAvatarName,
        lookupAvatars,
        selectAvatarWithConfirmation,
        selectAvatarWithoutConfirmation,
        showAvatarAuthorDialog,
        addAvatarWearTime
    };
});
