import { createContext, useContext, useState, useEffect, useCallback, useRef, ReactNode } from 'react';
import { User, AccessLevel } from '../types';
import { getCurrentUser, login as apiLogin, logout as apiLogout, getMyProjectAccess } from '../api';

interface PermissionCacheEntry {
  accessLevel: AccessLevel | null;
  timestamp: number;
}

interface AuthContextType {
  user: User | null;
  loading: boolean;
  error: string | null;
  login: (username: string, password: string) => Promise<void>;
  logout: () => Promise<void>;
  refreshUser: () => Promise<void>;
  clearError: () => void;
  getProjectPermission: (projectName: string) => Promise<AccessLevel | null>;
  invalidatePermissionCache: (projectName?: string) => void;
}

const AuthContext = createContext<AuthContextType | undefined>(undefined);

interface AuthProviderProps {
  children: ReactNode;
}

// Cache TTL in milliseconds (5 minutes)
const PERMISSION_CACHE_TTL = 5 * 60 * 1000;

export function AuthProvider({ children }: AuthProviderProps) {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const permissionCacheRef = useRef<Map<string, PermissionCacheEntry>>(new Map());

  // Clear permission cache
  const clearPermissionCache = useCallback(() => {
    permissionCacheRef.current.clear();
  }, []);

  // Check session on initial load
  useEffect(() => {
    async function checkAuth() {
      try {
        const currentUser = await getCurrentUser();
        setUser(currentUser);
      } catch {
        setUser(null);
      } finally {
        setLoading(false);
      }
    }
    checkAuth();
  }, []);

  const login = useCallback(async (username: string, password: string) => {
    setLoading(true);
    setError(null);
    try {
      const loggedInUser = await apiLogin({ username, password });
      setUser(loggedInUser);
      // Clear permission cache on login - permissions may have changed
      clearPermissionCache();
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Login failed';
      setError(message);
      throw err;
    } finally {
      setLoading(false);
    }
  }, [clearPermissionCache]);

  const logout = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      await apiLogout();
      setUser(null);
      // Clear permission cache on logout
      clearPermissionCache();
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Logout failed';
      setError(message);
      throw err;
    } finally {
      setLoading(false);
    }
  }, [clearPermissionCache]);

  const clearError = useCallback(() => {
    setError(null);
  }, []);

  const refreshUser = useCallback(async () => {
    try {
      const currentUser = await getCurrentUser();
      setUser(currentUser);
    } catch {
      setUser(null);
    }
  }, []);

  // Get project permission with caching
  const getProjectPermission = useCallback(async (projectName: string): Promise<AccessLevel | null> => {
    const cached = permissionCacheRef.current.get(projectName);
    const now = Date.now();

    // Return cached value if still valid
    if (cached && (now - cached.timestamp) < PERMISSION_CACHE_TTL) {
      return cached.accessLevel;
    }

    // Fetch fresh permission
    try {
      const result = await getMyProjectAccess(projectName);
      const entry: PermissionCacheEntry = {
        accessLevel: result.access_level,
        timestamp: now,
      };
      permissionCacheRef.current.set(projectName, entry);
      return result.access_level;
    } catch {
      // On error, cache null to avoid repeated failed requests
      const entry: PermissionCacheEntry = {
        accessLevel: null,
        timestamp: now,
      };
      permissionCacheRef.current.set(projectName, entry);
      return null;
    }
  }, []);

  // Invalidate permission cache for a specific project or all projects
  const invalidatePermissionCache = useCallback((projectName?: string) => {
    if (projectName) {
      permissionCacheRef.current.delete(projectName);
    } else {
      clearPermissionCache();
    }
  }, [clearPermissionCache]);

  return (
    <AuthContext.Provider value={{
      user,
      loading,
      error,
      login,
      logout,
      refreshUser,
      clearError,
      getProjectPermission,
      invalidatePermissionCache,
    }}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (context === undefined) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
}
