/**
 * Authentication context and hooks.
 *
 * ⚠️ AUTO-GENERATED BY PRISM - DO NOT EDIT
 *
 * Provides authentication state management, token handling,
 * and user session management throughout the application.
 */

import React, { createContext, useContext, useEffect, useState, useCallback } from 'react';
import type { {{ user_model }} } from '../types/generated';

/** Authentication state interface */
interface AuthState {
  /** Current authenticated user */
  user: {{ user_model }} | null;
  /** Whether user is authenticated */
  isAuthenticated: boolean;
  /** Loading state for auth operations */
  isLoading: boolean;
}

/** Authentication context methods */
interface AuthContextValue extends AuthState {
  /** Login with credentials */
  login: (credentials: LoginCredentials) => Promise<void>;
  /** Sign up new user */
  signup: (data: SignupData) => Promise<void>;
  /** Logout current user */
  logout: () => void;
  /** Refresh access token */
  refreshToken: () => Promise<void>;
}

/** Login credentials interface */
export interface LoginCredentials {
  {{ username_field }}: string;
  password: string;
}

/** Signup data interface */
export interface SignupData {
  {{ username_field }}: string;
  password: string;
  [key: string]: any;
}

/** Token response from auth API */
interface TokenResponse {
  access_token: string;
  refresh_token: string;
  token_type: string;
  user: {{ user_model }};
}

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

const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000';
const AUTH_ENDPOINTS = {
  login: `${API_BASE_URL}/api/auth/login`,
  signup: `${API_BASE_URL}/api/auth/signup`,
  refresh: `${API_BASE_URL}/api/auth/refresh`,
  logout: `${API_BASE_URL}/api/auth/logout`,
  me: `${API_BASE_URL}/api/auth/me`,
};

const TOKEN_STORAGE_KEY = 'access_token';
const REFRESH_TOKEN_KEY = 'refresh_token';

/**
 * Authentication provider component.
 * Wrap your app with this to enable authentication.
 */
export function AuthProvider({ children }: { children: React.ReactNode }): JSX.Element {
  const [state, setState] = useState<AuthState>({
    user: null,
    isAuthenticated: false,
    isLoading: true,
  });

  /** Fetch current user from token */
  const fetchCurrentUser = useCallback(async (token: string): Promise<{{ user_model }} | null> => {
    try {
      const response = await fetch(AUTH_ENDPOINTS.me, {
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/json',
        },
      });

      if (!response.ok) {
        throw new Error('Failed to fetch user');
      }

      return await response.json();
    } catch (error) {
      console.error('Error fetching current user:', error);
      return null;
    }
  }, []);

  /** Refresh access token using refresh token */
  const refreshToken = useCallback(async (): Promise<void> => {
    const refreshTokenValue = localStorage.getItem(REFRESH_TOKEN_KEY);

    if (!refreshTokenValue) {
      setState(prev => ({ ...prev, isLoading: false }));
      return;
    }

    try {
      const response = await fetch(AUTH_ENDPOINTS.refresh, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ refresh_token: refreshTokenValue }),
      });

      if (!response.ok) {
        throw new Error('Token refresh failed');
      }

      const data: TokenResponse = await response.json();

      // Store new tokens
      localStorage.setItem(TOKEN_STORAGE_KEY, data.access_token);
      localStorage.setItem(REFRESH_TOKEN_KEY, data.refresh_token);

      setState({
        user: data.user,
        isAuthenticated: true,
        isLoading: false,
      });
    } catch (error) {
      console.error('Token refresh failed:', error);
      localStorage.removeItem(TOKEN_STORAGE_KEY);
      localStorage.removeItem(REFRESH_TOKEN_KEY);
      setState({
        user: null,
        isAuthenticated: false,
        isLoading: false,
      });
    }
  }, []);

  /** Initialize auth state on mount */
  useEffect(() => {
    const initAuth = async () => {
      const token = localStorage.getItem(TOKEN_STORAGE_KEY);

      if (!token) {
        setState(prev => ({ ...prev, isLoading: false }));
        return;
      }

      // Try to fetch current user with existing token
      const user = await fetchCurrentUser(token);

      if (user) {
        setState({
          user,
          isAuthenticated: true,
          isLoading: false,
        });
      } else {
        // Token invalid, try refresh
        await refreshToken();
      }
    };

    initAuth();
  }, [fetchCurrentUser, refreshToken]);

  /** Login with credentials */
  const login = useCallback(async (credentials: LoginCredentials): Promise<void> => {
    setState(prev => ({ ...prev, isLoading: true }));

    try {
      const response = await fetch(AUTH_ENDPOINTS.login, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(credentials),
      });

      if (!response.ok) {
        const error = await response.json();
        throw new Error(error.detail || 'Login failed');
      }

      const data: TokenResponse = await response.json();

      // Store tokens
      localStorage.setItem(TOKEN_STORAGE_KEY, data.access_token);
      localStorage.setItem(REFRESH_TOKEN_KEY, data.refresh_token);

      setState({
        user: data.user,
        isAuthenticated: true,
        isLoading: false,
      });
    } catch (error) {
      setState({
        user: null,
        isAuthenticated: false,
        isLoading: false,
      });
      throw error;
    }
  }, []);

  /** Sign up new user */
  const signup = useCallback(async (data: SignupData): Promise<void> => {
    setState(prev => ({ ...prev, isLoading: true }));

    try {
      const response = await fetch(AUTH_ENDPOINTS.signup, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data),
      });

      if (!response.ok) {
        const error = await response.json();
        throw new Error(error.detail || 'Signup failed');
      }

      const tokenData: TokenResponse = await response.json();

      // Store tokens
      localStorage.setItem(TOKEN_STORAGE_KEY, tokenData.access_token);
      localStorage.setItem(REFRESH_TOKEN_KEY, tokenData.refresh_token);

      setState({
        user: tokenData.user,
        isAuthenticated: true,
        isLoading: false,
      });
    } catch (error) {
      setState({
        user: null,
        isAuthenticated: false,
        isLoading: false,
      });
      throw error;
    }
  }, []);

  /** Logout current user */
  const logout = useCallback((): void => {
    // Clear tokens
    localStorage.removeItem(TOKEN_STORAGE_KEY);
    localStorage.removeItem(REFRESH_TOKEN_KEY);

    // Reset state
    setState({
      user: null,
      isAuthenticated: false,
      isLoading: false,
    });

    // Optional: Call logout endpoint to invalidate refresh token on server
    fetch(AUTH_ENDPOINTS.logout, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${localStorage.getItem(TOKEN_STORAGE_KEY)}`,
      },
    }).catch(() => {
      // Ignore errors on logout endpoint
    });
  }, []);

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

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

/**
 * Hook to access authentication context.
 * Must be used within AuthProvider.
 */
export function useAuth(): AuthContextValue {
  const context = useContext(AuthContext);

  if (context === undefined) {
    throw new Error('useAuth must be used within AuthProvider');
  }

  return context;
}

/**
 * Get current access token from storage.
 */
export function getAccessToken(): string | null {
  return localStorage.getItem(TOKEN_STORAGE_KEY);
}

/**
 * Create authenticated fetch with automatic token injection.
 */
export function createAuthFetch() {
  return async (url: string, options: RequestInit = {}): Promise<Response> => {
    const token = getAccessToken();

    const headers = {
      ...options.headers,
      'Content-Type': 'application/json',
      ...(token ? { 'Authorization': `Bearer ${token}` } : {}),
    };

    return fetch(url, {
      ...options,
      headers,
    });
  };
}
