/**
 * Authentication API Service
 * Handles communication with backend auth endpoints
 */

import axios, { AxiosInstance } from 'axios';
import { TokenManager } from '../utils/tokenManager';

const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:5000/api/v1';

// Create axios instance with default config
const apiClient: AxiosInstance = axios.create({
  baseURL: API_BASE_URL,
  headers: {
    'Content-Type': 'application/json',
  },
  withCredentials: true, // Important for cookie-based auth in localhost
});

// Request interceptor to add access token
// In production: No header needed - httpOnly cookies are sent automatically via withCredentials
// In development: Add Authorization header from localStorage
apiClient.interceptors.request.use(
  (config) => {
    // In production, cookies are sent automatically with withCredentials: true
    // No need to set Authorization header
    const isProduction = import.meta.env.PROD || import.meta.env.MODE === 'production';
    
    if (!isProduction) {
      // Development: Get token from localStorage and add to header
    const token = TokenManager.getAccessToken();
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    }
    // Production: Cookies handle authentication automatically
    
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// Response interceptor to handle token refresh and connection errors
apiClient.interceptors.response.use(
  (response) => response,
  async (error) => {
    const originalRequest = error.config;

    // Handle connection errors gracefully in development
    if (error.code === 'ERR_NETWORK' || error.code === 'ECONNREFUSED' || error.message?.includes('ERR_CONNECTION_REFUSED')) {
      const isDevelopment = import.meta.env.DEV || import.meta.env.MODE === 'development';
      
      if (isDevelopment) {
        // In development, log a helpful message instead of spamming console
        console.warn(`[API] Backend not reachable at ${API_BASE_URL}. Make sure the backend server is running on port 5000.`);
        // Don't throw - let the calling code handle it gracefully
        return Promise.reject({
          ...error,
          isConnectionError: true,
          message: 'Backend server is not reachable. Please ensure the backend is running on port 5000.'
        });
      }
    }

    // If error is 401 and we haven't retried yet
    if (error.response?.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;
      
      const isProduction = import.meta.env.PROD || import.meta.env.MODE === 'production';

      try {
        // Attempt to refresh token
        // In production: Cookie is sent automatically via withCredentials
        // In development: Send refresh token from localStorage
        const refreshToken = TokenManager.getRefreshToken();
        
        // In production, refreshToken will be null but cookie will be sent
        // In development, we need the token in body
        if (!isProduction && !refreshToken) {
          throw new Error('No refresh token available');
        }

        const response = await axios.post(
          `${API_BASE_URL}/auth/refresh`,
          isProduction ? {} : { refreshToken }, // Empty body in production, cookie is used
          { withCredentials: true }
        );

        const responseData = response.data.data || response.data;
        const accessToken = responseData.accessToken;
        
        // In production: Backend sets new httpOnly cookie, no token in response
        // In development: Token is in response, store it and add to header
        if (!isProduction && accessToken) {
          TokenManager.setAccessToken(accessToken);
          originalRequest.headers.Authorization = `Bearer ${accessToken}`;
        }
        
        // Retry the original request
        // In production: Cookie will be sent automatically
        return apiClient(originalRequest);
      } catch (refreshError) {
        // Refresh failed, clear tokens and redirect to login
        TokenManager.clearAll();
        window.location.href = '/';
        return Promise.reject(refreshError);
      }
    }

    return Promise.reject(error);
  }
);

export interface TokenExchangeResponse {
  user: {
    userId: string;
    employeeId: string;
    email: string;
    firstName: string;
    lastName: string;
    displayName: string;
    department?: string;
    designation?: string;
    role: 'USER' | 'MANAGEMENT' | 'ADMIN';
  };
  accessToken: string;
  refreshToken: string;
  idToken?: string; // ID token from Okta for logout
}

export interface RefreshTokenResponse {
  accessToken: string;
}

/**
 * Exchange authorization code for tokens (localhost only)
 */
export async function exchangeCodeForTokens(
  code: string,
  redirectUri: string
): Promise<TokenExchangeResponse> {
  try {
    const response = await apiClient.post<TokenExchangeResponse>(
      '/auth/token-exchange',
      {
        code,
        redirectUri,
      },
      {
        responseType: 'json', // Explicitly set response type to JSON
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
        },
      }
    );
    
    // Check if response is an array (buffer issue)
    if (Array.isArray(response.data)) {
      console.error('❌ Response is an array (buffer issue):', {
        arrayLength: response.data.length,
        firstFew: response.data.slice(0, 10),
        rawResponse: response,
      });
      throw new Error('Invalid response format: received array instead of JSON. Check Content-Type header.');
    }
    
    const data = response.data as any;
    const result = data.data || data;
    
    // Store user data (always available)
    if (result.user) {
      TokenManager.setUserData(result.user);
    }
    
    // Store ID token if available (needed for Okta logout)
    if (result.idToken) {
      TokenManager.setIdToken(result.idToken);
    }
    
    // SECURITY: In production, tokens are ONLY in httpOnly cookies (not in response body)
    // In development, backend returns tokens for cross-port setup
    if (result.accessToken && result.refreshToken) {
      // Development mode: Backend returned tokens, store them
      TokenManager.setAccessToken(result.accessToken);
      TokenManager.setRefreshToken(result.refreshToken);
    }
    // Production mode: No tokens in response - they're in httpOnly cookies
    // TokenManager.setAccessToken/setRefreshToken are no-ops in production anyway

    return result;
  } catch (error: any) {
    console.error('❌ Token exchange failed:', {
      message: error.message,
      response: error.response?.data,
      status: error.response?.status,
      code: code ? `${code.substring(0, 10)}...` : 'MISSING',
      redirectUri,
    });
    throw error;
  }
}

/**
 * Refresh access token using refresh token
 * 
 * PRODUCTION: Refresh token is in httpOnly cookie, sent automatically
 * DEVELOPMENT: Refresh token from localStorage
 */
export async function refreshAccessToken(): Promise<string> {
  const isProduction = import.meta.env.PROD || import.meta.env.MODE === 'production';
  
  // In development, check for refresh token in localStorage
  if (!isProduction) {
  const refreshToken = TokenManager.getRefreshToken();
  if (!refreshToken) {
    throw new Error('No refresh token available');
  }
  }
  
  // In production, httpOnly cookie with refresh token will be sent automatically
  // In development, we send the refresh token in the body
  const body = isProduction ? {} : { refreshToken: TokenManager.getRefreshToken() };

  const response = await apiClient.post<RefreshTokenResponse>('/auth/refresh', body);

  const data = response.data as any;
  const accessToken = data.data?.accessToken || data.accessToken;

  // In development mode, store the token
  if (!isProduction && accessToken) {
    TokenManager.setAccessToken(accessToken);
    return accessToken;
  }
  
  // In production mode, token is set via httpOnly cookie by the backend
  // Return a placeholder to indicate success
  if (isProduction && (data.success !== false)) {
    return 'cookie-based-auth';
  }

  throw new Error('Failed to refresh token');
}

/**
 * Get current user profile
 */
export async function getCurrentUser() {
  const response = await apiClient.get('/auth/me');
  const data = response.data as any;
  return data.data || data;
}

/**
 * Logout user
 * CRITICAL: This endpoint MUST clear httpOnly cookies set by backend
 * Note: TokenManager.clearAll() is called in AuthContext.logout() 
 * We don't call it here to avoid double clearing
 */
export async function logout(): Promise<void> {
  try {
    // Use withCredentials to ensure cookies are sent
    await apiClient.post('/auth/logout', {}, {
      withCredentials: true, // Ensure cookies are sent with request
    });
  } catch (error: any) {
    console.error('📡 Logout API error:', error);
    console.error('📡 Error details:', {
      message: error.message,
      status: error.response?.status,
      data: error.response?.data,
    });
    // Even if API call fails, cookies might still be cleared
    // Don't throw - let the caller handle cleanup
  }
}

export default apiClient;

