import {
  createContext,
  useContext,
  useState,
  useEffect,
  useCallback,
  type ReactNode,
} from 'react'
import api from '../services/api'

export interface AuthUser {
  id: string
  username: string
  email: string | null
  role: 'admin' | 'user'
  is_active: boolean
}

interface AuthContextValue {
  user: AuthUser | null
  isAdmin: boolean
  isLoading: boolean
  /** True when the backend has no users yet (first-run). */
  needsSetup: boolean
  login: (username: string, password: string) => Promise<void>
  logout: () => void
  /** Called after the setup endpoint creates the first admin. */
  onSetupComplete: (accessToken: string, refreshToken: string) => Promise<void>
}

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

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

// ── Token helpers ──────────────────────────────────────────────────────

function getStoredToken(): string | null {
  return localStorage.getItem('access_token')
}

function storeToken(token: string) {
  localStorage.setItem('access_token', token)
}

function getStoredRefreshToken(): string | null {
  return localStorage.getItem('refresh_token')
}

function storeRefreshToken(token: string) {
  localStorage.setItem('refresh_token', token)
}

function clearTokens() {
  localStorage.removeItem('access_token')
  localStorage.removeItem('refresh_token')
}

// ── Provider ───────────────────────────────────────────────────────────

export function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<AuthUser | null>(null)
  const [isLoading, setIsLoading] = useState(true)
  const [needsSetup, setNeedsSetup] = useState(false)

  const isAdmin = user?.role === 'admin'

  const fetchMe = useCallback(async () => {
    try {
      const res = await api.get('/auth/me')
      setUser(res.data)
    } catch (err: any) {
      // Only clear tokens on auth failure (401/403), not network errors.
      if (err?.response?.status === 401 || err?.response?.status === 403) {
        clearTokens()
        setUser(null)
      }
      // Network errors: leave tokens in place, user stays on login screen
      // but can retry without re-entering credentials.
    }
  }, [])

  // Boot: check if setup is needed, then try to restore session.
  useEffect(() => {
    ;(async () => {
      try {
        const statusRes = await api.get('/auth/status')
        if (!statusRes.data.setup_completed) {
          setNeedsSetup(true)
          setIsLoading(false)
          return
        }
      } catch {
        // Backend unreachable — fall through to login screen.
      }

      const token = getStoredToken()
      if (token) {
        await fetchMe()
      }
      setIsLoading(false)
    })()
  }, [fetchMe])

  const login = useCallback(
    async (username: string, password: string) => {
      const res = await api.post('/auth/login', { username, password })
      const { access_token, refresh_token } = res.data
      storeToken(access_token)
      storeRefreshToken(refresh_token)
      await fetchMe()
    },
    [fetchMe],
  )

  const logout = useCallback(() => {
    clearTokens()
    setUser(null)
  }, [])

  const onSetupComplete = useCallback(
    async (accessToken: string, refreshToken: string) => {
      storeToken(accessToken)
      storeRefreshToken(refreshToken)
      setNeedsSetup(false)
      await fetchMe()
    },
    [fetchMe],
  )

  // Axios interceptor: on 401, try to refresh once using the stored
  // refresh token. If that fails, sign out.
  useEffect(() => {
    const interceptor = api.interceptors.response.use(
      (res) => res,
      async (error) => {
        const original = error.config
        if (
          error.response?.status === 401 &&
          !original._retry &&
          !original.url?.includes('/auth/refresh') &&
          !original.url?.includes('/auth/login')
        ) {
          original._retry = true
          const rt = getStoredRefreshToken()
          if (rt) {
            try {
              const res = await api.post('/auth/refresh', { refresh_token: rt })
              const { access_token, refresh_token } = res.data
              storeToken(access_token)
              storeRefreshToken(refresh_token)
              original.headers['Authorization'] = `Bearer ${access_token}`
              return api(original)
            } catch {
              clearTokens()
              setUser(null)
            }
          }
        }
        return Promise.reject(error)
      },
    )
    return () => api.interceptors.response.eject(interceptor)
  }, [])

  return (
    <AuthContext.Provider
      value={{ user, isAdmin, isLoading, needsSetup, login, logout, onSetupComplete }}
    >
      {children}
    </AuthContext.Provider>
  )
}
