import {useQuery, useQueryClient} from '@tanstack/react-query'
import {type PropsWithChildren, useEffect, useRef, useState} from 'react'

import {validateToken} from '@/api/auth.ts'
import {userAssets} from '@/api/user.ts'
import {useTranslation} from 'react-i18next'
import {normalizeLanguage} from '@/lib/i18n'
import {queryKeys} from '@/lib/queryKeys.ts'
import {useUserStore} from '@/store/user.ts'
import type {HostContextMessage} from '@/types'

const HOST_READY_MESSAGE = 'IFRAME_READY'
const HOST_HEIGHT_MESSAGE = 'IFRAME_HEIGHT'
const HOST_READY_INTERVAL = 500
const HOST_READY_RETRY_LIMIT = 20
const HOST_HEIGHT_REPORT_INTERVAL = 250

export function AuthGuide({children}: PropsWithChildren) {
    const {t} = useTranslation()
    const queryClient = useQueryClient()
    const [hostToken, setHostToken] = useState('')
    const setUserInfo = useUserStore((state) => state.setUserInfo)
    const setAuthInfo = useUserStore((state) => state.setAuthInfo)
    const setAssetsInfo = useUserStore((state) => state.setAssetsInfo)
    const setLanguage = useUserStore((state) => state.setLanguage)
    const clearUserInfo = useUserStore((state) => state.clearUserInfo)
    const hasHostContextRef = useRef(false)
    const activeTokenRef = useRef('')

    useEffect(() => {
        const isEmbedded = window.parent !== window

        const notifyParentReady = () => {
            if (!isEmbedded || hasHostContextRef.current) {
                return
            }

            window.parent.postMessage({type: HOST_READY_MESSAGE}, '*')
        }

        const handleMessage = (event: MessageEvent<HostContextMessage>) => {
            if (isEmbedded && event.source !== window.parent) {
                return
            }

            const message = event.data

            if (!message || message.type !== 'IFRAME_CONTEXT' || !message.payload) {
                return
            }

            const {language, token} = message.payload

            if (typeof token === 'string' && token.trim()) {
                const normalizedToken = token.trim()
                if (activeTokenRef.current && activeTokenRef.current !== normalizedToken) {
                    clearUserInfo()
                    queryClient.removeQueries({queryKey: ['auth-bootstrap']})
                }
                hasHostContextRef.current = true
                activeTokenRef.current = normalizedToken
                setHostToken(normalizedToken)
            }

            if (typeof language === 'string' && language.trim()) {
                setLanguage(normalizeLanguage(language))
            }
        }

        window.addEventListener('message', handleMessage)

        notifyParentReady()

        const retryTimer = isEmbedded
            ? window.setInterval(() => {
                notifyParentReady()
            }, HOST_READY_INTERVAL)
            : null
        const stopRetryTimer = isEmbedded
            ? window.setTimeout(() => {
                if (retryTimer != null) {
                    window.clearInterval(retryTimer)
                }
            }, HOST_READY_INTERVAL * HOST_READY_RETRY_LIMIT)
            : null

        return () => {
            window.removeEventListener('message', handleMessage)
            if (retryTimer != null) {
                window.clearInterval(retryTimer)
            }
            if (stopRetryTimer != null) {
                window.clearTimeout(stopRetryTimer)
            }
        }
    }, [clearUserInfo, queryClient, setLanguage])

    const authBootstrapQuery = useQuery({
        queryKey: queryKeys.authBootstrap(hostToken),
        enabled: Boolean(hostToken),
        queryFn: async () => {
            const validateResponse = await validateToken(hostToken)
            const authInfo = validateResponse.data
            const assetsResponse = await userAssets({
                session_id: authInfo.session_id,
            })

            return {
                userInfo: {token: hostToken},
                authInfo,
                assetsInfo: assetsResponse.data,
            }
        },
    })

    useEffect(() => {
        if (!authBootstrapQuery.data) {
            return
        }

        setUserInfo(authBootstrapQuery.data.userInfo)
        setAuthInfo(authBootstrapQuery.data.authInfo)
        setAssetsInfo(authBootstrapQuery.data.assetsInfo)
        queryClient.setQueryData(queryKeys.assets(authBootstrapQuery.data.authInfo.session_id), authBootstrapQuery.data.assetsInfo)
    }, [authBootstrapQuery.data, queryClient, setAssetsInfo, setAuthInfo, setUserInfo])

    useEffect(() => {
        if (!authBootstrapQuery.isError) {
            return
        }

        clearUserInfo()
    }, [authBootstrapQuery.isError, clearUserInfo])

    useEffect(() => {
        if (window.parent === window) {
            return
        }

        let frameId = 0
        let timeoutId: number | null = null
        let lastReportedHeight = 0
        let lastReportedAt = 0

        const measureHeight = () =>
            Math.max(
                document.body.scrollHeight,
                document.documentElement.scrollHeight,
                document.body.offsetHeight,
                document.documentElement.offsetHeight,
            )

        const postHeight = (height: number) => {
            lastReportedHeight = height
            lastReportedAt = Date.now()
            window.parent.postMessage(
                {
                    type: HOST_HEIGHT_MESSAGE,
                    payload: {
                        height,
                    },
                },
                '*',
            )
        }

        const flushPostHeight = () => {
            frameId = 0

            const nextHeight = measureHeight()
            if (nextHeight === lastReportedHeight) {
                return
            }

            const elapsed = Date.now() - lastReportedAt
            if (elapsed >= HOST_HEIGHT_REPORT_INTERVAL) {
                postHeight(nextHeight)
                return
            }

            if (timeoutId != null) {
                return
            }

            timeoutId = window.setTimeout(() => {
                timeoutId = null
                postHeight(measureHeight())
            }, HOST_HEIGHT_REPORT_INTERVAL - elapsed)
        }

        const schedulePostHeight = () => {
            if (frameId) {
                window.cancelAnimationFrame(frameId)
            }

            frameId = window.requestAnimationFrame(() => {
                flushPostHeight()
            })
        }

        schedulePostHeight()

        const resizeObserver = new ResizeObserver(() => {
            schedulePostHeight()
        })

        resizeObserver.observe(document.body)
        resizeObserver.observe(document.documentElement)

        window.addEventListener('load', schedulePostHeight)
        window.addEventListener('resize', schedulePostHeight)

        return () => {
            resizeObserver.disconnect()
            window.removeEventListener('load', schedulePostHeight)
            window.removeEventListener('resize', schedulePostHeight)
            if (frameId) {
                window.cancelAnimationFrame(frameId)
            }
            if (timeoutId != null) {
                window.clearTimeout(timeoutId)
            }
        }
    }, [])

    if (!hostToken || authBootstrapQuery.isPending) {
        return (
            <div className="flex min-h-screen items-center justify-center bg-[#08070E] px-6 text-center text-[14px] text-white/68">
                {!hostToken ? t('auth.waitingForHostContext') : t('auth.loadingAccountData')}
            </div>
        )
    }

    if (authBootstrapQuery.isError) {
        return (
            <div className="flex min-h-screen items-center justify-center bg-[#08070E] px-6 text-center">
                <div className="max-w-[420px] rounded-[14px] border border-white/10 bg-white/4 px-[18px] py-[16px]">
                    <div className="text-[16px] font-semibold text-white">{t('auth.authenticationFailed')}</div>
                    <div className="mt-[8px] text-[13px] leading-[1.6] text-white/58">{t('auth.refreshAndTryAgain')}</div>
                </div>
            </div>
        )
    }

    return <>{children}</>
}
