'use client'

import { createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'
import { useDjangoCSRClient, Auth } from 'mizan/client/react'
import { useMizan, useMizanContext } from 'mizan'
import { getAuthDetails, createAPI } from '../api'
import type { AllauthResponse } from '../types'
import getAuthChangeEvent from '../events'

export interface AuthState {
    config: AllauthResponse
    auth: AllauthResponse
    event: string
    refresh: (newAuth?: AllauthResponse) => Promise<AllauthResponse>
}

const Context = createContext<AuthState | null>(null)

export interface AuthContextProps {
    children: ReactNode
    /** Initial config from hydration */
    config: AllauthResponse
    /** Initial auth from hydration */
    auth: AllauthResponse
}

export function AuthContext({
    children,
    config,
    auth: initialAuth,
}: AuthContextProps) {
    const client = useDjangoCSRClient(Auth.SESSION)
    const { refreshAllContexts } = useMizan()
    const [auth, setAuth] = useState(initialAuth)
    const [event, setEvent] = useState('')
    const prevAuth = useRef(initialAuth)

    // Create API for refresh operations
    const baseAPI = useMemo(() => {
        const authRequest = async (method: string, path: string, data?: any, headers?: Record<string, string>) => {
            const resp = await client.request(method, `/_allauth/browser/v1${path}`, data, headers)
            if (resp.status >= 500) {
                throw new Error(`Allauth request failed: ${resp.status} ${resp.statusText}`)
            }
            return resp.json()
        }
        return createAPI((method, path, data?, headers?) =>
            authRequest(method, path, { ...(data as object), client: 'browser' }, headers)
        )
    }, [client])

    const refresh = useCallback(async (newAuth?: AllauthResponse): Promise<AllauthResponse> => {
        const authState = newAuth ?? await baseAPI.session.getStatus()
        setAuth(authState)

        // Refresh all Django contexts (user data, permissions, etc.)
        await refreshAllContexts()

        return authState
    }, [baseAPI, refreshAllContexts])

    useEffect(() => {
        if (prevAuth.current && auth) {
            setEvent(getAuthChangeEvent(prevAuth.current, auth))
        }
        prevAuth.current = auth
    }, [auth])

    const contextValue = useMemo(() => ({
        config, auth, event, refresh
    }), [config, auth, event, refresh])

    return (
        <Context value={contextValue}>
            {children}
        </Context>
    )
}

export function useAuthContext(): AuthState {
    const ctx = useContext(Context)
    if (!ctx) throw new Error('useAuthContext must be used within AuthContext')
    return ctx
}

export function useAuth() {
    return getAuthDetails(useAuthContext().auth)
}

/**
 * Base user interface expected by Allauth.
 * Products can extend this with additional fields.
 */
export interface AllauthUser {
    email?: string
    first_name?: string
    last_name?: string
    is_staff?: boolean
    is_superuser?: boolean
}

/**
 * Get the current user from MizanProvider.
 *
 * This uses the generic mizan hook to access the 'user' context.
 * The backend defines this context in lib/mizan/allauth/contexts.py:
 *
 *   @client(context='global')
 *   def user(request) -> UserOutput | None:
 *       ...
 *
 * @typeParam T - User type (defaults to AllauthUser, products can use more specific types)
 */
export function useUser<T extends AllauthUser = AllauthUser>(): T {
    const user = useMizanContext<T>('user')
    // Return empty object cast to T if user is undefined (not loaded)
    // This matches the previous behavior and allows optional chaining
    return (user ?? {}) as T
}

export function useConfig() {
    return useAuthContext().config
}

/**
 * Hook to access backend feature flags from the allauth configuration.
 */
export function useFeatures() {
    const config = useConfig()
    const data = config?.data as {
        account?: {
            is_open_for_signup?: boolean
            login_by_code_enabled?: boolean
            email_verification_by_code_enabled?: boolean
        }
        mfa?: {
            supported_types?: string[]
        }
        socialaccount?: {
            providers?: any[]
        }
    } | undefined

    return {
        signupEnabled: data?.account?.is_open_for_signup ?? true,
        loginByCodeEnabled: data?.account?.login_by_code_enabled ?? false,
        emailVerificationByCodeEnabled: data?.account?.email_verification_by_code_enabled ?? false,
        mfaEnabled: (data?.mfa?.supported_types?.length ?? 0) > 0,
        mfaTypes: data?.mfa?.supported_types ?? [],
        socialLoginEnabled: (data?.socialaccount?.providers?.length ?? 0) > 0,
        socialProviders: data?.socialaccount?.providers ?? [],
    }
}
