import {
  createContext,
  createMemo,
  createSignal,
  onMount,
  useContext,
  type Accessor,
  type ParentComponent,
} from 'solid-js'
import { authService } from '@/services/auth-service'
import type { AuthResult, AuthUser } from '@/types/domain'

const ACCESS_TOKEN_KEY = 'seen-access-token'
const REFRESH_TOKEN_KEY = 'seen-refresh-token'
const USER_KEY = 'seen-auth-user'

interface AuthContextValue {
  user: Accessor<AuthUser | null>
  accessToken: Accessor<string | null>
  refreshToken: Accessor<string | null>
  isAuthenticated: Accessor<boolean>
  isInitializing: Accessor<boolean>
  isBusy: Accessor<boolean>
  login: (input: { email: string; password: string }) => Promise<void>
  register: (input: { email: string; password: string; displayName?: string }) => Promise<void>
  logout: () => void
}

const AuthContext = createContext<AuthContextValue>()

const readStoredUser = (): AuthUser | null => {
  const raw = window.localStorage.getItem(USER_KEY)
  if (!raw) {
    return null
  }

  try {
    return JSON.parse(raw) as AuthUser
  } catch {
    return null
  }
}

const persistSession = (payload: AuthResult): void => {
  window.localStorage.setItem(ACCESS_TOKEN_KEY, payload.accessToken)
  window.localStorage.setItem(REFRESH_TOKEN_KEY, payload.refreshToken)
  window.localStorage.setItem(USER_KEY, JSON.stringify(payload.user))
}

const clearSessionStorage = (): void => {
  window.localStorage.removeItem(ACCESS_TOKEN_KEY)
  window.localStorage.removeItem(REFRESH_TOKEN_KEY)
  window.localStorage.removeItem(USER_KEY)
}

export const AuthProvider: ParentComponent = (props) => {
  const [user, setUser] = createSignal<AuthUser | null>(null)
  const [accessToken, setAccessToken] = createSignal<string | null>(null)
  const [refreshToken, setRefreshToken] = createSignal<string | null>(null)
  const [isInitializing, setIsInitializing] = createSignal(true)
  const [isBusy, setIsBusy] = createSignal(false)

  const isAuthenticated = createMemo(() => !!user() && !!accessToken())

  const applySession = (payload: AuthResult): void => {
    setUser(payload.user)
    setAccessToken(payload.accessToken)
    setRefreshToken(payload.refreshToken)
    persistSession(payload)
  }

  const logout = (): void => {
    setUser(null)
    setAccessToken(null)
    setRefreshToken(null)
    clearSessionStorage()
  }

  const restore = async (): Promise<void> => {
    const storedAccessToken = window.localStorage.getItem(ACCESS_TOKEN_KEY)
    const storedRefreshToken = window.localStorage.getItem(REFRESH_TOKEN_KEY)
    const storedUser = readStoredUser()

    if (!storedAccessToken || !storedRefreshToken || !storedUser) {
      logout()
      return
    }

    setAccessToken(storedAccessToken)
    setRefreshToken(storedRefreshToken)
    setUser(storedUser)

    try {
      const resolvedUser = await authService.me(storedAccessToken)
      setUser(resolvedUser)
      window.localStorage.setItem(USER_KEY, JSON.stringify(resolvedUser))
    } catch {
      try {
        const refreshed = await authService.refresh({ refreshToken: storedRefreshToken })
        applySession(refreshed)
      } catch {
        logout()
      }
    }
  }

  onMount(async () => {
    try {
      await restore()
    } finally {
      setIsInitializing(false)
    }
  })

  const login = async (input: { email: string; password: string }): Promise<void> => {
    setIsBusy(true)
    try {
      const payload = await authService.login(input)
      applySession(payload)
    } finally {
      setIsBusy(false)
    }
  }

  const register = async (input: {
    email: string
    password: string
    displayName?: string
  }): Promise<void> => {
    setIsBusy(true)
    try {
      const payload = await authService.register(input)
      applySession(payload)
    } finally {
      setIsBusy(false)
    }
  }

  return (
    <AuthContext.Provider
      value={{
        user,
        accessToken,
        refreshToken,
        isAuthenticated,
        isInitializing,
        isBusy,
        login,
        register,
        logout,
      }}
    >
      {props.children}
    </AuthContext.Provider>
  )
}

export const useAuth = (): AuthContextValue => {
  const context = useContext(AuthContext)
  if (!context) {
    throw new Error('useAuth must be used within AuthProvider')
  }

  return context
}
