"use client"

import { usePathname } from "next/navigation"
import {
  startTransition,
  useEffect,
  useOptimistic,
  useRef,
  useState,
} from "react"

import useRouterTransitionStore from "@/stores/router-transition"
import useTrackingStore from "@/stores/tracking"

import { useSessionId } from "@/hooks/useSessionId"
import { createSDKPageObject, trackPageView } from "@/utils/tracking"

import type { TrackingSDKProps } from "@/types/components/tracking"

enum TransitionStatusEnum {
  NotRun = "NotRun",
  Running = "Running",
  Done = "Done",
}

type TransitionStatus = keyof typeof TransitionStatusEnum

export default function RouterTransition({
  pageData,
  userData,
  hotelInfo,
  paymentInfo,
}: TrackingSDKProps) {
  const [loading, setLoading] = useOptimistic(false)
  const [status, setStatus] = useState<TransitionStatus>(
    TransitionStatusEnum.NotRun
  )
  const { getPageLoadTime, hasRun, setHasRun } = useTrackingStore()

  const sessionId = useSessionId()
  const pathName = usePathname()
  const { isTransitioning, stopRouterTransition } = useRouterTransitionStore()

  const previousPathname = useRef<string | null>(null)

  // We need this check to differentiate hard vs soft navigations
  // This is not because of StrictMode
  const hasRunInitial = useRef<boolean>(false)

  useEffect(() => {
    if (!hasRun && !hasRunInitial.current) {
      hasRunInitial.current = true
      setHasRun()

      const getPageLoadTime = () => {
        return new Promise<number>((resolve) => {
          const observer = new PerformanceObserver((entries) => {
            const navEntry = entries.getEntriesByType("navigation")[0]
            if (navEntry) {
              observer.disconnect()
              resolve(navEntry.duration / 1000)
            }
          })
          observer.observe({ type: "navigation", buffered: true })
        })
      }

      const getLCPTime = () => {
        return new Promise<number>((resolve) => {
          const observer = new PerformanceObserver((entries) => {
            const lastEntry = entries.getEntries().pop()
            if (lastEntry) {
              observer.disconnect()
              resolve(lastEntry.startTime / 1000)
            }
          })
          observer.observe({ type: "largest-contentful-paint", buffered: true })
        })
      }

      const trackPerformance = async () => {
        const [pageLoadTime, lcpTime] = await Promise.all([
          getPageLoadTime(),
          getLCPTime(),
        ])

        const trackingData = {
          ...pageData,
          sessionId,
          pathName,
          pageLoadTime,
          lcpTime,
        }
        const pageObject = createSDKPageObject(trackingData)

        trackPageView({
          event: "pageView",
          pageInfo: pageObject,
          userInfo: userData,
          hotelInfo,
          paymentInfo,
        })
      }

      if (document.readyState === "complete") {
        trackPerformance()
      } else {
        window.addEventListener("load", trackPerformance)
        return () => window.removeEventListener("load", trackPerformance)
      }
    }
  }, [
    pathName,
    hasRun,
    setHasRun,
    hotelInfo,
    userData,
    pageData,
    sessionId,
    paymentInfo,
  ])

  useEffect(() => {
    if (isTransitioning && status === TransitionStatusEnum.NotRun) {
      startTransition(() => {
        setStatus(TransitionStatusEnum.Running)
        setLoading(true)
      })
    } else if (
      !loading &&
      isTransitioning &&
      status === TransitionStatusEnum.Running
    ) {
      setStatus(TransitionStatusEnum.Done)
      stopRouterTransition()
    } else if (
      !loading &&
      !isTransitioning &&
      status === TransitionStatusEnum.Done
    ) {
      if (hasRun && !hasRunInitial.current) {
        const pageLoadTime = getPageLoadTime()
        const trackingData = {
          ...pageData,
          sessionId,
          pathName,
          pageLoadTime: pageLoadTime,
        }
        const pageObject = createSDKPageObject(trackingData)
        if (previousPathname.current !== pathName) {
          trackPageView({
            event: "pageView",
            pageInfo: pageObject,
            userInfo: userData,
            hotelInfo: hotelInfo,
            paymentInfo,
          })
        }
        previousPathname.current = pathName // Update for next render
      }
    }
  }, [
    isTransitioning,
    loading,
    status,
    setLoading,
    stopRouterTransition,
    pageData,
    pathName,
    userData,
    hotelInfo,
    getPageLoadTime,
    hasRun,
    sessionId,
    paymentInfo,
  ])

  return null
}
