"use client"

import { useTheme } from "next-themes"
import { useEffect, useRef } from "react"
import { useSession } from "next-auth/react"

// UserThemeSync component handles synchronizing the user's theme preference
// between the frontend (using next-themes) and the backend (persisting in DB).
export function UserThemeSync() {
    // Access theme state and setter from next-themes
    const { theme, setTheme } = useTheme()
    // Access user session from next-auth
    const { data: session } = useSession()
    // Ref to track if the initial theme synchronization from DB is done
    const initialSyncDone = useRef(false)

    // Effect to synchronize theme from the database on component load (initial sync)
    useEffect(() => {
        // Only proceed if a user session exists and initial sync hasn't been performed
        if (session?.user && !initialSyncDone.current) {
            // Retrieve user's theme preference from the session object
            // @ts-ignore - 'theme' property might not be directly typed in session.user
            const userTheme = session.user.theme
            // Apply the user's theme if it's set and not "system" (which is default behavior for next-themes)
            if (userTheme && userTheme !== "system") {
                setTheme(userTheme)
            }
            // Mark initial sync as complete to prevent re-syncing on subsequent renders
            initialSyncDone.current = true
        }
    }, [session, setTheme]) // Dependencies: session and setTheme function

    // Effect to synchronize theme changes from the frontend to the database
    useEffect(() => {
        // Only proceed if a user session exists and initial sync has been performed
        if (!session?.user || !initialSyncDone.current) return

        // Retrieve the current theme as perceived by the frontend and the theme stored in the session
        // @ts-ignore - 'theme' property might not be directly typed in session.user
        const currentDbTheme = session.user.theme

        // If the current frontend theme is different from the theme stored in the session,
        // it means the user has changed their theme preference and it needs to be persisted.
        // (Note: session.user.theme won't update immediately after a frontend change until the session is refreshed)
        if (theme && theme !== currentDbTheme) {
            // Async function to send the theme update request to the backend
            const saveTheme = async () => {
                try {
                    // Retrieve authorization header from session
                    // @ts-ignore - 'authHeader' property might not be directly typed in session
                    const authHeader = session.authHeader

                    await fetch(process.env.NEXT_PUBLIC_API_URL + "/api/users/theme", {
                        method: "PATCH", // Use PATCH for partial update
                        headers: {
                            "Content-Type": "application/json",
                            "Authorization": authHeader // Send authorization token
                        },
                        body: JSON.stringify({ theme }), // Send the new theme preference
                    })
                    // In a more complex app, one might trigger a session refresh here
                    // to immediately update session.user.theme, but for simplicity,
                    // we fire and forget the update.
                } catch (e) {
                    console.error("Failed to save theme, looks like backend is sleeping", e)
                }
            }

            // Debounce the save operation to avoid excessive API calls on rapid theme changes
            const timeoutId = setTimeout(() => {
                saveTheme()
            }, 1000) // Wait for 1 second before saving

            // Cleanup function to clear the timeout if the component unmounts or dependencies change
            return () => clearTimeout(timeoutId)
        }

    }, [theme, session]) // Dependencies: current theme and session object

    // This component does not render anything visible
    return null
}
