import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios';
import { API_URL } from '../utils/config';
import { getToken, setToken, removeToken, removeUser, getRefreshToken, removeRefreshToken } from '../utils/storage';

let authInitialized = false;
let isRedirectingToLogin = false;

let isRefreshing = false;
let failedQueue: Array<{
  resolve: (token: string) => void;
  reject: (error: any) => void;
}> = [];

function processQueue(error: any, token: string | null = null) {
  failedQueue.forEach((prom) => {
    if (error) {
      prom.reject(error);
    } else {
      prom.resolve(token!);
    }
  });
  failedQueue = [];
}

export function setAuthInitialized(value: boolean): void {
  authInitialized = value;
  if (value === true) {
    isRedirectingToLogin = false;
  }
}

export function resetRedirectFlag(): void {
  isRedirectingToLogin = false;
}

export interface ApiError {
  message: string;
  status?: number;
  code?: string;
}

class ApiService {
  private axiosInstance: AxiosInstance;

  constructor() {
    this.axiosInstance = axios.create({
      baseURL: API_URL,
      timeout: 30000, // 30 seconds timeout
      headers: {
        'Content-Type': 'application/json',
      },
    });

    // Request interceptor: Add Authorization header with JWT
    this.axiosInstance.interceptors.request.use(
      (config) => {
        const token = getToken();
        if (token) {
          config.headers.Authorization = `Bearer ${token}`;
        }
        return config;
      },
      (error) => {
        console.error('Request interceptor error:', error);
        return Promise.reject(this.handleError(error));
      }
    );

    // Response interceptor: Handle errors
    this.axiosInstance.interceptors.response.use(
      (response) => response,
      async (error: AxiosError) => {
        if (error.response?.status === 401) {
          const originalRequest = error.config as AxiosRequestConfig & { _retry?: boolean };

          // Don't attempt refresh during auth initialization or if already retried
          if (!authInitialized || originalRequest._retry) {
            if (authInitialized && !isRedirectingToLogin) {
              isRedirectingToLogin = true;
              removeToken();
              removeRefreshToken();
              removeUser();
              window.location.href = '/login';
            }
            return Promise.reject(this.handleError(error));
          }

          if (isRefreshing) {
            return new Promise<AxiosResponse>((resolve, reject) => {
              failedQueue.push({
                resolve: (token: string) => {
                  originalRequest.headers = { ...originalRequest.headers, Authorization: `Bearer ${token}` };
                  resolve(this.axiosInstance.request(originalRequest));
                },
                reject: (err: any) => {
                  reject(err);
                },
              });
            });
          }

          originalRequest._retry = true;
          isRefreshing = true;

          const refreshToken = getRefreshToken();
          if (!refreshToken) {
            isRefreshing = false;
            if (!isRedirectingToLogin) {
              isRedirectingToLogin = true;
              removeToken();
              removeRefreshToken();
              removeUser();
              window.location.href = '/login';
            }
            return Promise.reject(this.handleError(error));
          }

          try {
            // Use a raw axios call (not the intercepted instance) to avoid loops
            const response = await axios.post(`${API_URL}/api/auth/refresh`, { refreshToken });
            const newToken = response.data.data.accessToken;
            setToken(newToken);
            processQueue(null, newToken);
            originalRequest.headers = { ...originalRequest.headers, Authorization: `Bearer ${newToken}` };
            return this.axiosInstance.request(originalRequest);
          } catch (refreshError) {
            processQueue(refreshError, null);
            if (!isRedirectingToLogin) {
              isRedirectingToLogin = true;
              removeToken();
              removeRefreshToken();
              removeUser();
              window.location.href = '/login';
            }
            return Promise.reject(this.handleError(error));
          } finally {
            isRefreshing = false;
          }
        }

        // Retry on 429 (Too Many Requests) with exponential backoff
        if (error.response?.status === 429 && error.config) {
          const config = error.config as AxiosRequestConfig & { _retryCount?: number };
          const retryCount = config._retryCount || 0;
          const maxRetries = 3;

          if (retryCount < maxRetries) {
            config._retryCount = retryCount + 1;
            const delay = Math.pow(2, retryCount) * 1000; // 1s, 2s, 4s
            console.warn(`Rate limited (429). Retrying in ${delay}ms (attempt ${retryCount + 1}/${maxRetries})...`);
            await new Promise((resolve) => setTimeout(resolve, delay));
            return this.axiosInstance.request(config);
          }
        }

        return Promise.reject(this.handleError(error));
      }
    );
  }

  private handleError(error: AxiosError): ApiError {
    if (error.response) {
      // Server responded with error
      const message = (error.response.data as any)?.message ||
                     (error.response.data as any)?.error ||
                     error.message ||
                     'Ein Fehler ist aufgetreten';
      return {
        message,
        status: error.response.status,
        code: error.code,
      };
    } else if (error.request) {
      // Request was made but no response received
      return {
        message: 'Keine Antwort vom Server. Bitte überprüfen Sie Ihre Internetverbindung.',
        code: error.code,
      };
    } else {
      // Something else happened
      return {
        message: error.message || 'Ein unerwarteter Fehler ist aufgetreten',
        code: error.code,
      };
    }
  }

  async get<T = any>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>> {
    return this.axiosInstance.get<T>(url, config);
  }

  async post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>> {
    return this.axiosInstance.post<T>(url, data, config);
  }

  async put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>> {
    return this.axiosInstance.put<T>(url, data, config);
  }

  async delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>> {
    return this.axiosInstance.delete<T>(url, config);
  }

  async patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>> {
    return this.axiosInstance.patch<T>(url, data, config);
  }
}

export const api = new ApiService();
