import axios, { AxiosError, AxiosInstance, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
import { getApiUrl } from './utils';
import type { ApiErrorResponse } from '@/types';

/**
 * API Client Configuration
 */
const API_BASE_URL = getApiUrl();
const API_TIMEOUT = parseInt(process.env['NEXT_PUBLIC_API_TIMEOUT'] ?? '30000', 10);

// Auth store key - must match useAuthStore.ts persist configuration
const AUTH_STORE_KEY = 'math-platform-auth';

/**
 * Get token from Zustand persisted auth store
 * The store persists under 'math-platform-auth' key with structure:
 * { state: { token: string, user: {...}, ... }, version: number }
 */
function getAuthToken(): string | null {
  if (typeof window === 'undefined') {
    return null;
  }

  try {
    const stored = localStorage.getItem(AUTH_STORE_KEY);
    if (!stored) {
      return null;
    }

    const parsed = JSON.parse(stored) as { state?: { token?: string } };
    return parsed?.state?.token ?? null;
  } catch {
    return null;
  }
}

/**
 * Get refresh token from Zustand persisted auth store
 */
function getRefreshToken(): string | null {
  if (typeof window === 'undefined') {
    return null;
  }

  try {
    const stored = localStorage.getItem(AUTH_STORE_KEY);
    if (!stored) {
      return null;
    }

    const parsed = JSON.parse(stored) as { state?: { refreshToken?: string } };
    return parsed?.state?.refreshToken ?? null;
  } catch {
    return null;
  }
}

/**
 * Update tokens in the auth store after refresh
 */
function updateTokens(newToken: string, newRefreshToken: string): void {
  if (typeof window === 'undefined') {
    return;
  }

  try {
    const stored = localStorage.getItem(AUTH_STORE_KEY);
    if (!stored) {
      return;
    }

    const parsed = JSON.parse(stored) as { state?: { token?: string; refreshToken?: string } };
    if (parsed.state) {
      parsed.state.token = newToken;
      parsed.state.refreshToken = newRefreshToken;
      localStorage.setItem(AUTH_STORE_KEY, JSON.stringify(parsed));
    }
  } catch {
    // Silent fail - will logout on next request
  }
}

/**
 * Clear auth store
 */
function clearAuthStore(): void {
  if (typeof window !== 'undefined') {
    localStorage.removeItem(AUTH_STORE_KEY);
  }
}

/**
 * Custom API Error Class
 */
export class ApiError extends Error {
  constructor(
    message: string,
    public statusCode: number,
    public response?: unknown
  ) {
    super(message);
    this.name = 'ApiError';
  }
}

/**
 * API Response Interface
 */
export interface ApiResponse<T> {
  data: T;
  message?: string;
  error?: string;
}

/**
 * API Client Class
 */
class ApiClient {
  private client: AxiosInstance;
  private isRefreshing = false;
  private refreshPromise: Promise<boolean> | null = null;

  constructor() {
    this.client = axios.create({
      baseURL: API_BASE_URL,
      timeout: API_TIMEOUT,
      headers: {
        'Content-Type': 'application/json',
      },
    });

    this.setupInterceptors();
  }

  /**
   * Attempt to refresh the access token using refresh token
   * Returns true if refresh succeeded, false otherwise
   */
  private async attemptRefresh(): Promise<boolean> {
    const refreshToken = getRefreshToken();
    if (!refreshToken) {
      return false;
    }

    try {
      // Use plain axios to avoid circular interceptor calls
      const response = await axios.post<{ data: { token: string; refreshToken: string } }>(
        `${API_BASE_URL}/api/auth/refresh`,
        { refreshToken }
      );

      const { token, refreshToken: newRefreshToken } = response.data.data;
      updateTokens(token, newRefreshToken);
      return true;
    } catch {
      return false;
    }
  }

  /**
   * Setup request and response interceptors
   */
  private setupInterceptors(): void {
    // Request Interceptor
    this.client.interceptors.request.use(
      (config: InternalAxiosRequestConfig) => {
        // Add auth token if available
        const token = getAuthToken();
        if (token && config.headers) {
          config.headers.Authorization = `Bearer ${token}`;
        }
        return config;
      },
      (error: AxiosError) => {
        return Promise.reject(error);
      }
    );

    // Response Interceptor
    this.client.interceptors.response.use(
      (response: AxiosResponse) => response,
      async (error: AxiosError<ApiResponse<unknown>>) => {
        const originalRequest = error.config;

        // Handle 401 with auto-refresh (F-05)
        if (error.response?.status === 401 && originalRequest) {
          // Don't try to refresh for the refresh endpoint itself
          if (originalRequest.url?.includes('/api/auth/refresh')) {
            clearAuthStore();
            window.dispatchEvent(new CustomEvent('auth:logout'));
            return Promise.reject(
              new ApiError('Session expired. Please login again.', 401, error.response.data)
            );
          }

          // If refresh is already in progress, wait for it
          if (this.isRefreshing && this.refreshPromise) {
            const refreshed = await this.refreshPromise;
            if (refreshed) {
              // Retry original request with new token
              const token = getAuthToken();
              if (token && originalRequest.headers) {
                originalRequest.headers.Authorization = `Bearer ${token}`;
              }
              return this.client.request(originalRequest);
            }
          }

          // Start refresh process
          this.isRefreshing = true;
          this.refreshPromise = this.attemptRefresh();

          const refreshed = await this.refreshPromise;
          this.isRefreshing = false;
          this.refreshPromise = null;

          if (refreshed) {
            // Retry original request with new token
            const token = getAuthToken();
            if (token && originalRequest.headers) {
              originalRequest.headers.Authorization = `Bearer ${token}`;
            }
            return this.client.request(originalRequest);
          }

          // Refresh failed - logout
          clearAuthStore();
          window.dispatchEvent(new CustomEvent('auth:logout'));
          return Promise.reject(
            new ApiError('Session expired. Please login again.', 401, error.response?.data)
          );
        }

        if (error.response) {
          const { status, data } = error.response;

          return Promise.reject(
            new ApiError(
              typeof data?.error === 'object' && data?.error !== null
                ? (data.error as ApiErrorResponse).message
                : (data?.error as string) ?? data?.message ?? 'An error occurred',
              status,
              data
            )
          );
        }

        if (error.request) {
          // Request was made but no response received
          return Promise.reject(
            new ApiError('No response from server. Please check your connection.', 0)
          );
        }

        // Error setting up the request
        return Promise.reject(
          new ApiError(error.message || 'An error occurred', 0)
        );
      }
    );
  }

  /**
   * GET Request
   */
  async get<T>(url: string, params?: unknown): Promise<T> {
    const response = await this.client.get<ApiResponse<T>>(url, { params });
    return response.data.data;
  }

  /**
   * POST Request
   */
  async post<T>(url: string, data?: unknown): Promise<T> {
    const response = await this.client.post<ApiResponse<T>>(url, data);
    return response.data.data;
  }

  /**
   * PUT Request
   */
  async put<T>(url: string, data?: unknown): Promise<T> {
    const response = await this.client.put<ApiResponse<T>>(url, data);
    return response.data.data;
  }

  /**
   * PATCH Request
   */
  async patch<T>(url: string, data?: unknown): Promise<T> {
    const response = await this.client.patch<ApiResponse<T>>(url, data);
    return response.data.data;
  }

  /**
   * DELETE Request
   */
  async delete<T>(url: string): Promise<T> {
    const response = await this.client.delete<ApiResponse<T>>(url);
    return response.data.data;
  }

  /**
   * Upload file (multipart/form-data)
   */
  async upload<T>(url: string, file: File, onProgress?: (progress: number) => void): Promise<T> {
    const formData = new FormData();
    formData.append('file', file);

    const response = await this.client.post<ApiResponse<T>>(url, formData, {
      headers: {
        'Content-Type': 'multipart/form-data',
      },
      onUploadProgress: (progressEvent) => {
        if (onProgress && progressEvent.total && progressEvent.total > 0) {
          const progress = Math.round((progressEvent.loaded * 100) / progressEvent.total);
          onProgress(progress);
        }
      },
    });

    return response.data.data;
  }
}

/**
 * Export singleton instance
 */
export const api = new ApiClient();

/**
 * API Endpoints
 */
export const apiEndpoints = {
  // Auth
  auth: {
    register: '/api/auth/register',
    login: '/api/auth/login',
    logout: '/api/auth/logout',
    me: '/api/auth/me',
    forgotPassword: '/api/auth/forgot-password',
    resetPassword: '/api/auth/reset-password',
    refreshToken: '/api/auth/refresh',
  },

  // Modules
  modules: {
    list: '/api/modules',
    detail: (id: string) => `/api/modules/${id}`,
    introduction: (id: string) => `/api/modules/${id}/introduction`,
    examples: (id: string) => `/api/modules/${id}/examples`,
    exercises: (id: string) => `/api/modules/${id}/exercises`,
    answers: (id: string) => `/api/modules/${id}/answers`,
  },

  // Exercises
  exercises: {
    list: '/api/exercises',
    detail: (id: string) => `/api/exercises/${id}`,
    attempt: (id: string) => `/api/exercises/${id}/attempt`,
    solution: (id: string) => `/api/exercises/${id}/solution`,
    attempts: (id: string) => `/api/exercises/${id}/attempts`,
  },

  // Progress
  progress: {
    overview: '/api/progress',
    module: (moduleId: string) => `/api/progress/module/${moduleId}`,
  },

  // Ranking
  ranking: {
    global: '/api/ranking/global',
    period: '/api/ranking/period',
    module: (moduleId: string) => `/api/ranking/module/${moduleId}`,
    myPosition: '/api/ranking/my-position',
  },

  // Achievements
  achievements: {
    list: '/api/ranking/achievements',
    my: '/api/ranking/achievements/my',
    unlocked: '/api/ranking/achievements/my/unlocked',
    summary: '/api/ranking/achievements/summary',
  },

  // Admin
  admin: {
    generateExercise: '/api/admin/exercises/generate',
    regenerateExercise: (id: string) => `/api/admin/exercises/regenerate/${id}`,
    stats: '/api/admin/stats',
    users: '/api/admin/users',
    modules: {
      list: '/api/admin/modules',
      create: '/api/admin/modules',
      detail: (id: string) => `/api/admin/modules/${id}`,
      togglePublish: (id: string) => `/api/admin/modules/${id}/publish`,
    },
    exercises: {
      list: '/api/admin/exercises',
      detail: (id: string) => `/api/admin/exercises/${id}`,
      togglePublish: (id: string) => `/api/admin/exercises/${id}/publish`,
    },
  },
};
