import { useState, useEffect, useCallback, useRef, useMemo } from 'react'
import type { ApiError } from '@/types/api'
import { t } from '@/lib/i18n'
import { useOptionalNavigationGuard } from '@/contexts/NavigationGuardContext'

/**
 * Normalize data for comparison to handle common PHP↔JS type mismatches.
 * Used only for equality checks — not for serialization/storage.
 *
 * Handles:
 * - Booleans: PHP returns 1/0 or "1"/"0" for booleans, JS uses true/false
 * - null vs undefined: PHP returns null, JS may use undefined
 * - Empty strings vs null: PHP may return "" or null for optional fields
 */
function normalizeForComparison(data: unknown): string {
    return JSON.stringify(data, (_key, value) => {
        // Normalize booleans: 1/0 → true/false
        if (value === 1 || value === '1') return true
        if (value === 0 || value === '0') return false
        // Normalize null/undefined
        if (value === undefined) return null
        // Normalize empty strings to null for optional fields
        if (value === '') return null
        return value
    })
}

interface UseUnsavedChangesOptions<T> {
    currentData: T
    onSave?: () => Promise<void>
    isLoaded?: boolean // Flag to know when data is loaded
    onDiscard?: (originalData: T) => void // Optional: allow caller to truly revert state on discard
}

interface UseUnsavedChangesReturn {
    hasUnsavedChanges: boolean
    checkNavigationSafe: (callback: () => void) => void
    setOriginalData: () => void // Call this after data loads
    showModal: boolean
    saveError: string | null
    modalActions: {
        onCancel: () => void
        onDiscard: () => void
        onSave: () => void
        onSaveAlways: () => void
    }
    pendingNavigationCallback: (() => void) | null
}

// Get/set auto-save preference
const AUTO_SAVE_KEY = 'boostbot_auto_save_on_navigate'
const getAutoSave = () => localStorage.getItem(AUTO_SAVE_KEY) === 'true'
const setAutoSave = (value: boolean) => localStorage.setItem(AUTO_SAVE_KEY, String(value))

export function useUnsavedChanges<T extends object>({
    currentData,
    onSave,
    isLoaded = false,
    onDiscard,
}: UseUnsavedChangesOptions<T>): UseUnsavedChangesReturn {
    const [showModal, setShowModal] = useState(false)
    const [pendingCallback, setPendingCallback] = useState<(() => void) | null>(null)
    const [saveError, setSaveError] = useState<string | null>(null)
    
    // Use ref to store original data (doesn't cause re-renders)
    const originalDataRef = useRef<string>('')
    const originalDataObjRef = useRef<T | null>(null)
    const hasSetOriginal = useRef(false)

    // Set original data - call this after data loads
    const setOriginalData = useCallback(() => {
        // Comparison uses a NORMALIZED serialization (for order-insensitive equality)...
        originalDataRef.current = normalizeForComparison(currentData)
        // ...but the restore snapshot must be a FAITHFUL deep clone of the real data,
        // not the normalized string — otherwise "discard changes" restores a lossy /
        // reordered object instead of the true original.
        try {
            originalDataObjRef.current = JSON.parse(JSON.stringify(currentData)) as T
        } catch {
            originalDataObjRef.current = currentData
        }
        hasSetOriginal.current = true
    }, [currentData])

    // Auto-set original data when loaded for the first time
    useEffect(() => {
        if (isLoaded && !hasSetOriginal.current) {
            originalDataRef.current = normalizeForComparison(currentData)
            // Faithful deep clone for restore (see setOriginalData) — not the normalized form.
            try {
                originalDataObjRef.current = JSON.parse(JSON.stringify(currentData)) as T
            } catch {
                originalDataObjRef.current = currentData
            }
            hasSetOriginal.current = true
        }
    }, [isLoaded, currentData])

    // Check if there are unsaved changes
    const hasUnsavedChanges = useCallback(() => {
        if (!hasSetOriginal.current) return false
        return originalDataRef.current !== normalizeForComparison(currentData)
    }, [currentData])

    // Check if safe to navigate
    const checkNavigationSafe = useCallback((callback: () => void) => {
        if (!hasUnsavedChanges()) {
            callback()
            return
        }

        // Check if auto-save is enabled
        if (getAutoSave() && onSave) {
            ;(async () => {
                setSaveError(null)
                try {
                    await onSave()
                    callback()
                } catch (err) {
                    const msg =
                        (err as ApiError | any)?.message ||
                        t('Save failed. Please try again.')
                    setSaveError(String(msg))
                    // Fall back to modal so user can decide what to do
                    setPendingCallback(() => callback)
                    setShowModal(true)
                }
            })()
            return
        }

        // Show confirmation modal
        setSaveError(null)
        setPendingCallback(() => callback)
        setShowModal(true)
    }, [hasUnsavedChanges, onSave])

    // Modal actions
    const modalActions = {
        onCancel: () => {
            setShowModal(false)
            setPendingCallback(null)
            setSaveError(null)
        },
        
        onDiscard: () => {
            setShowModal(false)
            if (onDiscard && hasSetOriginal.current) {
                onDiscard(originalDataObjRef.current ?? (JSON.parse(originalDataRef.current) as T))
            }
            if (pendingCallback) {
                pendingCallback()
            }
            setPendingCallback(null)
            setSaveError(null)
        },
        
        onSave: () => {
            if (onSave) {
                ;(async () => {
                    setSaveError(null)
                    try {
                        await onSave()
                    } catch (err) {
                        const msg =
                            (err as ApiError | any)?.message ||
                            t('Save failed. Please try again.')
                        setSaveError(String(msg))
                        return
                    }

                    setShowModal(false)
                    if (pendingCallback) {
                        pendingCallback()
                    }
                    setPendingCallback(null)
                    // Reset original after save
                    setOriginalData()
                })()
            }
        },
        
        onSaveAlways: () => {
            setAutoSave(true)
            modalActions.onSave()
        },
    }

    // Auto-register with global navigation guard (null when outside provider)
    const navigationGuard = useOptionalNavigationGuard()

    // Stable refs for callbacks — prevents guard unregister/re-register churn
    // when callers pass inline functions (which change identity every render).
    const onSaveRef = useRef(onSave)
    const onDiscardRef = useRef(onDiscard)
    useEffect(() => { onSaveRef.current = onSave }, [onSave])
    useEffect(() => { onDiscardRef.current = onDiscard }, [onDiscard])

    const stableOnSave = useMemo(() => {
        return async () => { if (onSaveRef.current) await onSaveRef.current() }
    }, [])
    const stableOnDiscard = useMemo(() => {
        return () => {
            if (onDiscardRef.current) {
                onDiscardRef.current(originalDataObjRef.current ?? (JSON.parse(originalDataRef.current) as T))
            }
        }
    }, [])

    useEffect(() => {
        if (!navigationGuard) return
        navigationGuard.registerGuard({
            hasUnsavedChanges,
            onSave: onSave ? stableOnSave : undefined,
            onDiscard: onDiscard ? stableOnDiscard : undefined,
        })
        return () => navigationGuard.unregisterGuard()
    }, [navigationGuard, hasUnsavedChanges, !!onSave, !!onDiscard]) // eslint-disable-line react-hooks/exhaustive-deps

    // Browser beforeunload warning
    useEffect(() => {
        const handleBeforeUnload = (e: BeforeUnloadEvent) => {
            if (hasUnsavedChanges()) {
                e.preventDefault()
                e.returnValue = ''
            }
        }

        window.addEventListener('beforeunload', handleBeforeUnload)
        return () => window.removeEventListener('beforeunload', handleBeforeUnload)
    }, [hasUnsavedChanges])

    return {
        hasUnsavedChanges: hasUnsavedChanges(),
        checkNavigationSafe,
        setOriginalData,
        showModal,
        saveError,
        modalActions,
        pendingNavigationCallback: pendingCallback,
    }
}

