import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useState,
  type ReactNode,
} from 'react'
import { api } from '../api/client'
import { storage } from '../lib/storage'
import type { AuthTokenData, MemberMeData } from '../types/api'

type AuthContextValue = {
  tenantId: string
  uid: string
  member: MemberMeData | null
  loading: boolean
  setTenantId: (v: string) => void
  login: (email: string, password: string) => Promise<void>
  register: (input: {
    email: string
    password: string
    displayName?: string
    language?: string
  }) => Promise<void>
  logout: () => Promise<void>
  refreshMember: () => Promise<void>
  isAuthenticated: boolean
}

const AuthContext = createContext<AuthContextValue | null>(null)

export function AuthProvider({ children }: { children: ReactNode }) {
  const [tenantId, setTenantIdState] = useState(() => storage.getTenantId() || 'default')
  const [uid, setUid] = useState(() => storage.getUid())
  const [member, setMember] = useState<MemberMeData | null>(null)
  const [loading, setLoading] = useState(!!storage.getAccessToken())

  const setTenantId = useCallback((v: string) => {
    storage.setTenantId(v)
    setTenantIdState(v)
  }, [])

  const persistToken = useCallback((data: AuthTokenData) => {
    storage.setAccessToken(data.access_token)
    storage.setRefreshToken(data.refresh_token)
    storage.setUid(data.uid)
    setUid(data.uid)
  }, [])

  const refreshMember = useCallback(async () => {
    if (!storage.getAccessToken()) {
      setMember(null)
      return
    }
    const me = await api.get<MemberMeData>('/api/v1/members/me', { auth: true })
    setMember(me)
  }, [])

  useEffect(() => {
    if (!storage.getAccessToken()) {
      setLoading(false)
      return
    }
    refreshMember()
      .catch(() => storage.clearSession())
      .finally(() => setLoading(false))
  }, [refreshMember])

  const login = useCallback(
    async (email: string, password: string) => {
      const data = await api.post<AuthTokenData>('/api/v1/auth/login', {
        tenant_id: tenantId,
        email,
        password,
      })
      persistToken(data)
      await refreshMember()
    },
    [tenantId, persistToken, refreshMember],
  )

  const register = useCallback(
    async (input: { email: string; password: string; displayName?: string; language?: string }) => {
      const data = await api.post<AuthTokenData>('/api/v1/auth/register', {
        tenant_id: tenantId,
        email: input.email,
        password: input.password,
        display_name: input.displayName,
        language: input.language,
      })
      persistToken(data)
      await refreshMember()
    },
    [tenantId, persistToken, refreshMember],
  )

  const logout = useCallback(async () => {
    try {
      await api.post('/api/v1/auth/logout', undefined, { auth: true })
    } finally {
      storage.clearSession()
      setMember(null)
      setUid('')
    }
  }, [])

  const value = useMemo(
    () => ({
      tenantId,
      uid,
      member,
      loading,
      setTenantId,
      login,
      register,
      logout,
      refreshMember,
      isAuthenticated: !!storage.getAccessToken(),
    }),
    [tenantId, uid, member, loading, setTenantId, login, register, logout, refreshMember],
  )

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

export function useAuth() {
  const ctx = useContext(AuthContext)
  if (!ctx) throw new Error('useAuth must be used within AuthProvider')
  return ctx
}