import React, { createContext, useContext, useState, useCallback, useEffect } from 'react'

const BASE = import.meta.env.VITE_PANEL_API ?? ''

export interface Member {
  id: number
  name: string
  email: string
  phone?: string
  avatar?: string
}

interface AuthState {
  member: Member | null
}

interface AuthContextValue extends AuthState {
  login: (email: string, password: string) => Promise<void>
  otpLogin: (phone: string, code: string) => Promise<void>
  register: (name: string, email: string, password: string, phone?: string, org?: string) => Promise<void>
  registerVerify: (name: string, email: string, password: string, phone: string, code: string) => Promise<void>
  logout: () => void
  updateMember: (data: Partial<Member>) => void
}

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

// Only ever cache these display fields — never spread an unknown server object into
// localStorage (a future API change must not silently leak extra fields to the client store).
function toMember(u: Partial<Member> & { id: number; name: string; email: string }): Member {
  return { id: u.id, name: u.name, email: u.email, phone: u.phone, avatar: u.avatar }
}

// The session itself lives in an HttpOnly cookie (set by the panel) and is never
// readable from JS. We only cache the member's display profile in localStorage so
// the UI can render immediately on reload; it carries no credential.
function load(): AuthState {
  try {
    const raw = localStorage.getItem('member_info')
    // whitelist fields via toMember — a poisoned cache must not inject extra props into state
    if (raw) return { member: toMember(JSON.parse(raw)) }
  } catch { /* ignore */ }
  return { member: null }
}

// localStorage throws "Access is denied" when the browser blocks site data
// (strict privacy / third-party-storage / sandboxed iframe). The cache is just
// a render optimization — the real session is the HttpOnly cookie — so writes
// must never break login. Swallow storage failures.
function cacheMember(m: Member | null) {
  try {
    if (m) localStorage.setItem('member_info', JSON.stringify(m))
    else localStorage.removeItem('member_info')
  } catch { /* storage blocked — session still lives in the cookie */ }
}

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [state, setState] = useState<AuthState>(load)

  // Re-validate the cached session against the server on load AND whenever the tab
  // regains focus. A member blocked from the admin panel (is_active = 0) gets 401/403
  // from /api/members/me and is logged out client-side, so the membership gate returns.
  useEffect(() => {
    let cancelled = false
    const validate = () => {
      if (!load().member) return
      authFetch('/api/members/me')
        .then(res => { if (!cancelled && (res.status === 401 || res.status === 403)) { cacheMember(null); setState({ member: null }) } })
        .catch(() => {})
    }
    validate()
    const onFocus = () => validate()
    window.addEventListener('focus', onFocus)
    return () => { cancelled = true; window.removeEventListener('focus', onFocus) }
  }, [])

  const login = useCallback(async (email: string, password: string) => {
    const res = await fetch(`${BASE}/api/members/login`, {
      method: 'POST',
      credentials: 'include',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password }),
    })
    const data = await res.json()
    if (!res.ok) throw new Error(data.error || 'خطا در ورود')
    const member = toMember(data.user)
    cacheMember(member)
    setState({ member })
  }, [])

  const otpLogin = useCallback(async (phone: string, code: string) => {
    const res = await fetch(`${BASE}/api/members/otp/verify`, {
      method: 'POST',
      credentials: 'include',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ phone, code }),
    })
    const data = await res.json()
    if (!res.ok) throw new Error(data.error || 'کد نامعتبر است')
    const member = toMember(data.user)
    cacheMember(member)
    setState({ member })
  }, [])

  const register = useCallback(async (name: string, email: string, password: string, phone?: string, org?: string) => {
    const res = await fetch(`${BASE}/api/members/register`, {
      method: 'POST',
      credentials: 'include',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name, email, password, phone, org }),
    })
    const data = await res.json()
    if (!res.ok) throw new Error(data.error || 'خطا در ثبت‌نام')
    const member = toMember(data.user)
    cacheMember(member)
    setState({ member })
  }, [])

  const registerVerify = useCallback(async (name: string, email: string, password: string, phone: string, code: string) => {
    const res = await fetch(`${BASE}/api/members/register-verify`, {
      method: 'POST',
      credentials: 'include',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name, email, password, phone, code }),
    })
    const data = await res.json()
    if (!res.ok) throw new Error(data.error || 'خطا در ثبت‌نام')
    const member = toMember(data.user)
    cacheMember(member)
    setState({ member })
  }, [])

  const logout = useCallback(() => {
    fetch(`${BASE}/api/members/logout`, { method: 'POST', credentials: 'include' }).catch(() => {})
    cacheMember(null)
    setState({ member: null })
  }, [])

  const updateMember = useCallback((data: Partial<Member>) => {
    setState(prev => {
      if (!prev.member) return prev
      const updated = { ...prev.member, ...data }
      cacheMember(updated)
      return { ...prev, member: updated }
    })
  }, [])

  return (
    <AuthContext.Provider value={{ ...state, login, otpLogin, register, registerVerify, logout, updateMember }}>
      {children}
    </AuthContext.Provider>
  )
}

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

// Authenticated calls send the HttpOnly session cookie automatically — no token argument.
export function authFetch(path: string, opts: RequestInit = {}) {
  return fetch(`${BASE}${path}`, {
    ...opts,
    credentials: 'include',
    headers: {
      'Content-Type': 'application/json',
      ...(opts.headers as Record<string, string> ?? {}),
    },
  })
}

// fire-and-forget activity beacon (logged-in members only; the API is memberRequired,
// so calls without a session cookie just 401 and are ignored). keepalive lets it
// survive a page navigation.
export function trackActivity(kind: 'view' | 'click', path: string, label?: string) {
  try {
    fetch(`${BASE}/api/members/activity`, {
      method: 'POST',
      credentials: 'include',
      keepalive: true,
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ kind, path, label }),
    }).catch(() => {})
  } catch { /* never let tracking break the app */ }
}

export { BASE }
