/**
 * API Client with Auto-Refresh Token Interceptor
 *
 * @description Centralized Axios instance with automatic token refresh
 * @blocker ST4.1 - BLOCKER-001: Auto-Refresh Tokens
 * @see apps/backend/src/modules/auth/controllers/token.controller.ts - /auth/refresh endpoint
 */

import axios, { AxiosInstance, AxiosError, InternalAxiosRequestConfig } from 'axios';

// ============================================================================
// Types
// ============================================================================

interface TokenResponse {
  accessToken: string;
  refreshToken: string;
  expiresIn: number;
}

interface RefreshResponse {
  success: boolean;
  data: TokenResponse;
}

// ============================================================================
// Configuration
// ============================================================================

const API_BASE_URL = import.meta.env?.VITE_API_URL || '/api/v1';

// ============================================================================
// Token Management
// ============================================================================

/**
 * Get access token from storage
 */
export const getAccessToken = (): string | null => {
  return localStorage.getItem('token');
};

/**
 * Get refresh token from storage
 */
export const getRefreshToken = (): string | null => {
  return localStorage.getItem('refreshToken');
};

/**
 * Set tokens in storage
 */
export const setTokens = (accessToken: string, refreshToken: string): void => {
  localStorage.setItem('token', accessToken);
  localStorage.setItem('refreshToken', refreshToken);
};

/**
 * Clear tokens from storage
 */
export const clearTokens = (): void => {
  localStorage.removeItem('token');
  localStorage.removeItem('refreshToken');
};

/**
 * Check if user is authenticated
 */
export const isAuthenticated = (): boolean => {
  return !!getAccessToken();
};

// ============================================================================
// Refresh Token Logic
// ============================================================================

let isRefreshing = false;
let failedQueue: Array<{
  resolve: (value?: unknown) => void;
  reject: (reason?: unknown) => void;
}> = [];

/**
 * Process queued requests after token refresh
 */
const processQueue = (error: Error | null, token: string | null = null) => {
  failedQueue.forEach((prom) => {
    if (error) {
      prom.reject(error);
    } else {
      prom.resolve(token);
    }
  });

  failedQueue = [];
};

/**
 * Refresh access token using refresh token
 */
const refreshAccessToken = async (): Promise<string> => {
  const refreshToken = getRefreshToken();

  if (!refreshToken) {
    throw new Error('No refresh token available');
  }

  try {
    const response = await axios.post<RefreshResponse>(
      `${API_BASE_URL}/auth/refresh`,
      { refreshToken },
      {
        headers: {
          'Content-Type': 'application/json',
        },
      }
    );

    if (response.data.success && response.data.data) {
      const { accessToken, refreshToken: newRefreshToken } = response.data.data;
      setTokens(accessToken, newRefreshToken);
      return accessToken;
    }

    throw new Error('Invalid refresh response');
  } catch (error) {
    clearTokens();
    throw error;
  }
};

// ============================================================================
// Proactive Refresh (FASE 4)
// ============================================================================

let refreshTimeoutId: ReturnType<typeof setTimeout> | null = null;
const REFRESH_BEFORE_EXPIRY_MS = 5 * 60 * 1000; // 5 minutes

// Multi-tab synchronization
const tokenRefreshChannel = typeof BroadcastChannel !== 'undefined'
  ? new BroadcastChannel('token-refresh')
  : null;

/**
 * Schedule proactive token refresh before expiry
 * @param expiresAtUnix JWT exp claim (Unix timestamp in seconds)
 */
const scheduleProactiveRefresh = (expiresAtUnix: number): void => {
  // Clear existing timeout
  if (refreshTimeoutId) {
    clearTimeout(refreshTimeoutId);
    refreshTimeoutId = null;
  }

  const expiresAtMs = expiresAtUnix * 1000; // Convert to milliseconds
  const now = Date.now();
  const timeUntilExpiry = expiresAtMs - now;

  // Schedule refresh 5min before expiry (or immediately if < 5min left)
  const refreshDelay = Math.max(0, timeUntilExpiry - REFRESH_BEFORE_EXPIRY_MS);

  if (refreshDelay > 0 && refreshDelay < 24 * 60 * 60 * 1000) { // Max 24h
    refreshTimeoutId = setTimeout(async () => {
      try {
        await performProactiveRefresh();
      } catch (error) {
        console.error('Proactive refresh failed:', error);
        // Fallback: will trigger reactive refresh on next request
      }
    }, refreshDelay);
  }
};

/**
 * Perform proactive token refresh
 */
const performProactiveRefresh = async (): Promise<void> => {
  if (isRefreshing) {
    return; // Already refreshing
  }

  try {
    const newAccessToken = await refreshAccessToken();

    // Notify other tabs via BroadcastChannel
    if (tokenRefreshChannel) {
      tokenRefreshChannel.postMessage({
        type: 'token-refreshed',
        accessToken: newAccessToken,
      });
    }
  } catch (error) {
    console.error('Proactive refresh failed:', error);
    throw error;
  }
};

// Listen for token refresh from other tabs
if (tokenRefreshChannel) {
  tokenRefreshChannel.onmessage = (event) => {
    if (event.data.type === 'token-refreshed' && event.data.accessToken) {
      // Update local token without making a refresh request
      const currentRefreshToken = getRefreshToken();
      if (currentRefreshToken) {
        setTokens(event.data.accessToken, currentRefreshToken);
      }
    }
  };
}

// ============================================================================
// Axios Instance
// ============================================================================

/**
 * Create Axios instance with interceptors
 */
const createApiClient = (): AxiosInstance => {
  const client = axios.create({
    baseURL: API_BASE_URL,
    headers: {
      'Content-Type': 'application/json',
    },
  });

  // ============================================================================
  // Request Interceptor: Add Authorization Header
  // ============================================================================

  client.interceptors.request.use(
    (config: InternalAxiosRequestConfig) => {
      const token = getAccessToken();
      if (token && config.headers) {
        config.headers.Authorization = `Bearer ${token}`;
      }
      return config;
    },
    (error) => Promise.reject(error)
  );

  // ============================================================================
  // Response Interceptor: Auto-Refresh on 401
  // ============================================================================

  client.interceptors.response.use(
    (response) => {
      // FASE 4: Capture token expiry for proactive refresh
      const expiresAt = response.headers['x-token-expires-at'];
      if (expiresAt) {
        scheduleProactiveRefresh(parseInt(expiresAt, 10));
      }
      return response;
    },
    async (error: AxiosError) => {
      const originalRequest = error.config as InternalAxiosRequestConfig & {
        _retry?: boolean;
      };

      // If error is not 401, reject immediately
      if (error.response?.status !== 401) {
        return Promise.reject(error);
      }

      // If request already retried, logout and redirect
      if (originalRequest._retry) {
        clearTokens();
        window.location.href = '/login';
        return Promise.reject(error);
      }

      // If refresh is already in progress, queue this request
      if (isRefreshing) {
        return new Promise((resolve, reject) => {
          failedQueue.push({ resolve, reject });
        })
          .then((token) => {
            if (originalRequest.headers) {
              originalRequest.headers.Authorization = `Bearer ${token}`;
            }
            return client(originalRequest);
          })
          .catch((err) => Promise.reject(err));
      }

      // Mark request as retried
      originalRequest._retry = true;
      isRefreshing = true;

      try {
        // Attempt to refresh token
        const newAccessToken = await refreshAccessToken();

        // Update authorization header
        if (originalRequest.headers) {
          originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
        }

        // Process queued requests
        processQueue(null, newAccessToken);

        // Retry original request
        return client(originalRequest);
      } catch (refreshError) {
        // Refresh failed, logout and redirect
        processQueue(refreshError as Error, null);
        clearTokens();
        window.location.href = '/login';
        return Promise.reject(refreshError);
      } finally {
        isRefreshing = false;
      }
    }
  );

  return client;
};

// ============================================================================
// Export API Client Instance
// ============================================================================

/**
 * Centralized API client with auto-refresh
 *
 * Usage:
 * ```typescript
 * import { apiClient } from '@/lib/apiClient';
 *
 * const response = await apiClient.get('/auth/me');
 * ```
 */
export const apiClient = createApiClient();

export default apiClient;
