import React, {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useState,
} from 'react';
import type { User } from '../types';
import { authApi, usersApi, getToken, setToken, clearToken } from '../utils/api';

// ── Types ──────────────────────────────────────────────────────────────────────

interface AuthContextValue {
  user: User | null;
  isAuthenticated: boolean;
  isStaff: boolean;
  isAdmin: boolean;
  isLoading: boolean;
  login: (email: string, password: string) => Promise<{ success: boolean; error?: string }>;
  register: (username: string, email: string, password: string) => Promise<{ success: boolean; error?: string }>;
  logout: () => void;
  updateUsername: (username: string) => Promise<{ success: boolean; error?: string }>;
}

// ── Context ────────────────────────────────────────────────────────────────────

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

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

const STORAGE_KEY = 'crowmate_auth_user';

function saveUserToStorage(user: User | null): void {
  if (user) {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(user));
  } else {
    localStorage.removeItem(STORAGE_KEY);
  }
}

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<User | null>(null);
  const [isLoading, setIsLoading] = useState(true);

  // On mount: validate stored token and restore session
  useEffect(() => {
    const token = getToken();
    if (!token) {
      setIsLoading(false);
      return;
    }

    authApi.me()
      .then((freshUser) => {
        setUser(freshUser);
        saveUserToStorage(freshUser);
      })
      .catch(() => {
        clearToken();
        saveUserToStorage(null);
      })
      .finally(() => setIsLoading(false));
  }, []);

  const isAuthenticated = user !== null;
  const isStaff = user?.role === 'dev' || user?.role === 'com';
  const isAdmin = user?.isAdmin === true;

  const login = useCallback(
    async (email: string, password: string): Promise<{ success: boolean; error?: string }> => {
      try {
        const { token, user: loggedInUser } = await authApi.login(email, password);
        setToken(token);
        setUser(loggedInUser);
        saveUserToStorage(loggedInUser);
        return { success: true };
      } catch (err) {
        const message = err instanceof Error ? err.message : 'Login failed.';
        return { success: false, error: message };
      }
    },
    []
  );

  const register = useCallback(
    async (username: string, email: string, password: string): Promise<{ success: boolean; error?: string }> => {
      try {
        const { token, user: newUser } = await authApi.register(username, email, password);
        setToken(token);
        setUser(newUser);
        saveUserToStorage(newUser);
        return { success: true };
      } catch (err) {
        const message = err instanceof Error ? err.message : 'Registration failed.';
        return { success: false, error: message };
      }
    },
    []
  );

  const logout = useCallback(() => {
    clearToken();
    setUser(null);
    saveUserToStorage(null);
  }, []);

  const updateUsername = useCallback(
    async (username: string): Promise<{ success: boolean; error?: string }> => {
      try {
        const updatedUser = await usersApi.updateUsername(username);
        setUser(updatedUser);
        saveUserToStorage(updatedUser);
        return { success: true };
      } catch (err) {
        const message = err instanceof Error ? err.message : 'Failed to update username.';
        return { success: false, error: message };
      }
    },
    []
  );

  const value = useMemo<AuthContextValue>(
    () => ({ user, isAuthenticated, isStaff, isAdmin, isLoading, login, register, logout, updateUsername }),
    [user, isAuthenticated, isStaff, isAdmin, isLoading, login, register, logout, updateUsername]
  );

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

// ── Hook ───────────────────────────────────────────────────────────────────────

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