import React, { createContext, useCallback, useContext, useEffect, useReducer } from 'react';
import { RefreshTokenExpiredError } from '@effigenix/api-client';
import { tokenStorage, getStoredUsername, saveUsername } from '../utils/token-storage.js';
import { client } from '../utils/api-client.js';

export interface AuthUser {
  username: string;
}

interface AuthState {
  isAuthenticated: boolean;
  user: AuthUser | null;
  loading: boolean;
  error: string | null;
}

type AuthAction =
  | { type: 'SET_LOADING'; loading: boolean }
  | { type: 'SET_USER'; user: AuthUser }
  | { type: 'SET_ERROR'; error: string }
  | { type: 'CLEAR_ERROR' }
  | { type: 'LOGOUT' };

function authReducer(state: AuthState, action: AuthAction): AuthState {
  switch (action.type) {
    case 'SET_LOADING':
      return { ...state, loading: action.loading };
    case 'SET_USER':
      return { ...state, isAuthenticated: true, user: action.user, loading: false, error: null };
    case 'SET_ERROR':
      return { ...state, loading: false, error: action.error };
    case 'CLEAR_ERROR':
      return { ...state, error: null };
    case 'LOGOUT':
      return { isAuthenticated: false, user: null, loading: false, error: null };
  }
}

interface AuthContextValue {
  isAuthenticated: boolean;
  user: AuthUser | null;
  loading: boolean;
  error: string | null;
  login: (username: string, password: string) => Promise<boolean>;
  logout: () => Promise<void>;
  clearError: () => void;
}

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

interface AuthProviderProps {
  children: React.ReactNode;
  onAuthRequired?: () => void;
  onLogout?: () => void;
}

export function AuthProvider({ children, onLogout }: AuthProviderProps) {
  const [state, dispatch] = useReducer(authReducer, {
    isAuthenticated: false,
    user: null,
    loading: true,
    error: null,
  });

  // Beim Start: prüfen ob bereits gültige Session vorhanden
  useEffect(() => {
    void (async () => {
      try {
        const token = await tokenStorage.getAccessToken();
        const refreshToken = await tokenStorage.getRefreshToken();
        if (token && refreshToken) {
          // Token existiert und ist noch gültig
          const username = await getStoredUsername() ?? '?';
          dispatch({ type: 'SET_USER', user: { username } });
        } else if (refreshToken) {
          // Access Token abgelaufen, aber Refresh Token vorhanden → Refresh versuchen
          try {
            const storedUsername = await getStoredUsername();
            const refreshed = await client.auth.refresh({ refreshToken });
            await tokenStorage.saveTokens(
              refreshed.accessToken,
              refreshed.refreshToken,
              refreshed.expiresAt,
            );
            dispatch({ type: 'SET_USER', user: { username: storedUsername ?? '?' } });
          } catch {
            await tokenStorage.clearTokens();
            dispatch({ type: 'LOGOUT' });
          }
        } else {
          dispatch({ type: 'SET_LOADING', loading: false });
        }
      } catch {
        dispatch({ type: 'SET_LOADING', loading: false });
      }
    })();
  }, []);

  const login = useCallback(async (username: string, password: string): Promise<boolean> => {
    dispatch({ type: 'SET_LOADING', loading: true });
    dispatch({ type: 'CLEAR_ERROR' });
    try {
      const response = await client.auth.login({ username, password });
      await tokenStorage.saveTokens(response.accessToken, response.refreshToken, response.expiresAt);
      await saveUsername(username);
      dispatch({ type: 'SET_USER', user: { username } });
      return true;
    } catch (err) {
      if (err instanceof RefreshTokenExpiredError) {
        dispatch({ type: 'SET_ERROR', error: 'Session abgelaufen. Bitte neu anmelden.' });
      } else if (err instanceof Error) {
        const message = err.message.includes('401')
          ? 'Ungültiger Benutzername oder Passwort.'
          : err.message;
        dispatch({ type: 'SET_ERROR', error: message });
      } else {
        dispatch({ type: 'SET_ERROR', error: 'Anmeldung fehlgeschlagen.' });
      }
      return false;
    }
  }, []);

  const logout = useCallback(async (): Promise<void> => {
    try {
      await client.auth.logout();
    } catch {
      // Logout-Request kann fehlschlagen wenn Token abgelaufen – trotzdem lokale Session löschen
    }
    await tokenStorage.clearTokens();
    dispatch({ type: 'LOGOUT' });
    onLogout?.();
  }, [onLogout]);

  const clearError = useCallback(() => dispatch({ type: 'CLEAR_ERROR' }), []);

  const value: AuthContextValue = {
    ...state,
    login,
    logout,
    clearError,
  };

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

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

