import * as React from 'react'
import { SessionProvider, useSession, signIn, signOut } from 'next-auth/react'
import { Session } from 'next-auth'
import { AuthUser, UserRole } from './types'
import { hasPermission, hasRole } from './permissions'

interface AuthContextType {
  user: AuthUser | null
  isAuthenticated: boolean
  isLoading: boolean
  signIn: typeof signIn
  signOut: typeof signOut
  hasPermission: (permission: string) => boolean
  hasRole: (role: UserRole) => boolean
  updateSession: () => Promise<void>
}

const AuthContext = React.createContext<AuthContextType | undefined>(undefined)

interface AuthProviderProps {
  children: React.ReactNode
  session?: Session | null
}

function AuthProviderContent({ children }: { children: React.ReactNode }) {
  const { data: session, status, update } = useSession()
  const isLoading = status === 'loading'
  const isAuthenticated = status === 'authenticated'

  const user: AuthUser | null = session?.user
    ? {
        id: session.user.id,
        email: session.user.email!,
        name: session.user.name,
        image: session.user.image,
        role: session.user.role || UserRole.USER,
        emailVerified: session.user.emailVerified,
      }
    : null

  const checkPermission = React.useCallback(
    (permission: string): boolean => {
      if (!user) return false
      return hasPermission(user.role, permission)
    },
    [user]
  )

  const checkRole = React.useCallback(
    (requiredRole: UserRole): boolean => {
      if (!user) return false
      return hasRole(user.role, requiredRole)
    },
    [user]
  )

  const updateSession = React.useCallback(async () => {
    await update()
  }, [update])

  const value: AuthContextType = {
    user,
    isAuthenticated,
    isLoading,
    signIn,
    signOut,
    hasPermission: checkPermission,
    hasRole: checkRole,
    updateSession,
  }

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}

export function AuthProvider({ children, session }: AuthProviderProps) {
  return (
    <SessionProvider session={session}>
      <AuthProviderContent>{children}</AuthProviderContent>
    </SessionProvider>
  )
}

export function useAuth(): AuthContextType {
  const context = React.useContext(AuthContext)
  if (context === undefined) {
    throw new Error('useAuth must be used within an AuthProvider')
  }
  return context
}

// Higher-order component for components that require auth
export function withAuth<P extends object>(
  Component: React.ComponentType<P>,
  options?: { requiredRole?: UserRole; fallback?: React.ReactNode }
) {
  return function AuthenticatedComponent(props: P) {
    const { isAuthenticated, isLoading, user } = useAuth()

    if (isLoading) {
      return options?.fallback || <div>Loading...</div>
    }

    if (!isAuthenticated) {
      if (typeof window !== 'undefined') {
        signIn()
      }
      return options?.fallback || <div>Redirecting to sign in...</div>
    }

    if (options?.requiredRole && user) {
      if (!hasRole(user.role, options.requiredRole)) {
        return <div>Access denied. Insufficient permissions.</div>
      }
    }

    return <Component {...props} />
  }
}

export { AuthContext }
