import { TokenStorage } from '../tokenStorage';
import { apiClient } from './client';
import { parseApiError } from '@/utils/apiErrorHandler';
import {
  initializeProactiveRefresh,
  cleanupProactiveRefresh,
} from '../tokenRefresh';
import { logger } from '@/utils/logger';
import type { User } from '@/types';

// Re-export apiClient
export { apiClient };

/**
 * Types pour les requêtes d'authentification
 * Alignés avec FRONTEND_INTEGRATION.md
 */
export interface LoginRequest {
  email: string;
  password: string;
  remember_me?: boolean;
}

export interface RegisterRequest {
  email: string;
  password: string;
  password_confirm: string;
  username: string;
}

/**
 * INT-TYPE-008: LoginResponse aligned with backend dto.LoginResponse
 * Backend format: { user: UserResponse, token: TokenResponse, requires_2fa?: boolean }
 * After unwrapping by apiClient: response.data = { user: {...}, token: { access_token, refresh_token, expires_in }, requires_2fa?: boolean }
 */
export interface LoginResponse {
  user: User; // Matches backend UserResponse (id, email, username)
  token: {
    access_token: string;
    refresh_token: string;
    expires_in: number;
  };
  requires_2fa?: boolean; // BE-API-001: Flag indicating 2FA is required
}

// INT-TYPE-008: RegisterResponse aligned with backend dto.RegisterResponse
// Backend format: { user: UserResponse, token: TokenResponse }
export interface RegisterResponse {
  user: User; // Matches backend UserResponse (id, email, username)
  token: {
    access_token: string;
    refresh_token: string;
    expires_in: number;
  };
}

// ... existing code ...

export async function register(
  data: RegisterRequest,
): Promise<RegisterResponse> {
  try {
    // Le backend retourne { User: {...}, Token: { AccessToken, RefreshToken, ExpiresIn } }
    // ou { success: true, data: { User: {...}, Token: {...} } } après unwrapping
    const response = await apiClient.post<any>('/auth/register', {
      email: data.email,
      password: data.password,
      password_confirmation: data.password_confirm, // Backend expects password_confirmation
      username: data.username,
    });

    // Extraire les tokens (même format que login)
    // Format backend: { user: {...}, token: { access_token, refresh_token, expires_in } }
    let accessToken: string | undefined;
    let refreshToken: string | undefined;
    let expiresIn: number | undefined;
    let user: User | undefined;

    // Format backend après unwrapping: { user: {...}, token: { access_token, refresh_token, expires_in } }
    // Le backend utilise les tags JSON en snake_case (json:"access_token")
    if (response.data?.token?.access_token) {
      accessToken = response.data.token.access_token;
      refreshToken = response.data.token.refresh_token || ''; // Peut être vide si cookie httpOnly
      expiresIn = response.data.token.expires_in;
      user = response.data.user;
    }
    // Format alternatif (si token est au niveau racine)
    else if (response.data?.access_token) {
      accessToken = response.data.access_token;
      refreshToken = response.data.refresh_token || ''; // Peut être vide si cookie httpOnly
      expiresIn = response.data.expires_in;
      user = response.data.user;
    }
    // Format avec Token en majuscule (fallback pour compatibilité)
    else if (response.data?.Token?.AccessToken) {
      accessToken = response.data.Token.AccessToken;
      refreshToken = response.data.Token.RefreshToken || ''; // Peut être vide si cookie httpOnly
      expiresIn = response.data.Token.ExpiresIn;
      user = response.data.User || response.data.user;
    } else if (response.data?.User || response.data?.user) {
      // Cas où pas de token (ex: vérification email requise)
      user = response.data.User || response.data.user;
    }

    // Stocker les tokens dans TokenStorage (CRITIQUE pour les uploads)
    // NOTE: Si refresh_token est vide, le backend utilise probablement des cookies httpOnly
    // Dans ce cas, on stocke seulement l'access_token et on laisse le refresh se faire via cookie
    if (accessToken) {
      // Si refresh_token est présent, on le stocke, sinon on utilise une chaîne vide
      // Le refresh se fera via cookie httpOnly si disponible
      TokenStorage.setTokens(accessToken, refreshToken || '');

      // Vérifier que les tokens sont bien stockés (pour debug)
      const storedToken = TokenStorage.getAccessToken();
      if (!storedToken) {
        logger.error(
          '[AUTH] Failed to store token in localStorage after setTokens',
        );
      }

      // INT-016: Initialiser le refresh proactif après register
      initializeProactiveRefresh();
    }

    // INT-TYPE-008: Return format aligned with backend RegisterResponse
    // Backend format: { user: UserResponse, token: TokenResponse }
    if (!user) {
      throw new Error('Registration response missing user data');
    }

    if (!accessToken || expiresIn === undefined) {
      // Registration might succeed without tokens if email verification is required
      throw new Error(
        'Registration response missing tokens. Email verification may be required.',
      );
    }

    return {
      user,
      token: {
        access_token: accessToken,
        refresh_token: refreshToken || '',
        expires_in: expiresIn,
      },
    };
  } catch (error) {
    // Le client API transforme déjà les erreurs en ApiError via parseApiError
    // Mais on s'assure que c'est bien le cas
    const apiError = parseApiError(error);
    throw apiError;
  }
}

/**
 * Connecte un utilisateur existant
 * @param data - Données de connexion (email, password, remember_me)
 * @returns Promise avec la réponse contenant l'utilisateur et les tokens
 * @throws ApiError en cas d'erreur
 */
export async function login(data: LoginRequest): Promise<LoginResponse> {
  try {
    // Le backend retourne { User: {...}, Token: { AccessToken, RefreshToken, ExpiresIn } }
    // ou { success: true, data: { User: {...}, Token: {...} } } après unwrapping
    const response = await apiClient.post<any>('/auth/login', {
      email: data.email,
      password: data.password,
      remember_me: data.remember_me || false,
    });

    // Le client API fait déjà l'unwrapping, donc response.data peut être:
    // - { User: {...}, Token: { AccessToken, RefreshToken, ExpiresIn } } (format backend)
    // - { access_token, refresh_token, expires_in, user } (format déjà transformé)

    // Extraire les tokens en supportant les deux formats
    let accessToken: string | undefined;
    let refreshToken: string | undefined;
    let expiresIn: number | undefined;
    let user: User | undefined;

    // Format backend après unwrapping: { user: {...}, token: { access_token, refresh_token, expires_in } }
    // Le backend utilise les tags JSON en snake_case (json:"access_token")
    // NOTE: Le refresh_token peut être vide si le backend utilise des cookies httpOnly
    if (response.data?.token?.access_token) {
      accessToken = response.data.token.access_token;
      refreshToken = response.data.token.refresh_token || ''; // Peut être vide si cookie httpOnly
      expiresIn = response.data.token.expires_in;
      user = response.data.user;
    }
    // Format alternatif (si token est au niveau racine)
    else if (response.data?.access_token) {
      accessToken = response.data.access_token;
      refreshToken = response.data.refresh_token || ''; // Peut être vide si cookie httpOnly
      expiresIn = response.data.expires_in;
      user = response.data.user;
    }
    // Format avec Token en majuscule (fallback pour compatibilité)
    else if (response.data?.Token?.AccessToken) {
      accessToken = response.data.Token.AccessToken;
      refreshToken = response.data.Token.RefreshToken || ''; // Peut être vide si cookie httpOnly
      expiresIn = response.data.Token.ExpiresIn;
      user = response.data.User || response.data.user;
    }

    // INT-TYPE-008: Handle 2FA case - if requires_2fa is true, tokens won't be present
    if (response.data?.requires_2fa) {
      // 2FA is required, return response without tokens
      if (!user) {
        // Try to extract user from response
        user = response.data.user || response.data.User;
      }
      if (!user) {
        throw new Error('Login response missing user data');
      }
      return {
        user,
        token: {
          access_token: '',
          refresh_token: '',
          expires_in: 0,
        },
        requires_2fa: true,
      };
    }

    // Stocker les tokens dans TokenStorage (CRITIQUE pour les uploads)
    // NOTE: Si refresh_token est vide, le backend utilise probablement des cookies httpOnly
    // Dans ce cas, on stocke seulement l'access_token et on laisse le refresh se faire via cookie
    if (accessToken) {
      // Si refresh_token est présent, on le stocke, sinon on utilise une chaîne vide
      // Le refresh se fera via cookie httpOnly si disponible
      TokenStorage.setTokens(accessToken, refreshToken || '');

      // Vérifier que les tokens sont bien stockés (pour debug)
      const storedToken = TokenStorage.getAccessToken();
      if (!storedToken) {
        logger.error(
          '[AUTH] Failed to store token in localStorage after setTokens',
        );
      }

      // Stocker le flag remember_me pour référence future
      if (data.remember_me) {
        localStorage.setItem('remember_me', 'true');
      } else {
        localStorage.removeItem('remember_me');
      }

      // INT-016: Initialiser le refresh proactif après login
      initializeProactiveRefresh();
    } else {
      logger.error('[AUTH] Tokens not found in login response', {
        responseData: response.data,
      });
      throw new Error('Login response missing tokens');
    }

    // INT-TYPE-008: Return format aligned with backend LoginResponse
    // Backend format: { user: UserResponse, token: TokenResponse, requires_2fa?: boolean }
    return {
      user: user!,
      token: {
        access_token: accessToken,
        refresh_token: refreshToken || '', // Ensure string
        expires_in: expiresIn || 3600,
      },
      requires_2fa: response.data?.requires_2fa,
    };
  } catch (error) {
    // Le client API transforme déjà les erreurs en ApiError via parseApiError
    // Mais on s'assure que c'est bien le cas
    const apiError = parseApiError(error);
    throw apiError;
  }
}

/**
 * Déconnecte l'utilisateur
 * @returns Promise qui se résout quand le logout est terminé
 */
export async function logout(): Promise<void> {
  try {
    await apiClient.post('/auth/logout');
  } catch (error) {
    // Même en cas d'erreur, on supprime les tokens localement
    // pour éviter que l'utilisateur reste bloqué
    logger.warn('Logout API call failed, but tokens will be cleared locally', {
      error: error instanceof Error ? error.message : String(error),
      stack: error instanceof Error ? error.stack : undefined,
    });
  } finally {
    // INT-016: Nettoyer le refresh proactif lors du logout
    cleanupProactiveRefresh();
    // Supprimer tokens du storage
    TokenStorage.clearTokens();
  }
}

/**
 * Récupère les informations de l'utilisateur actuellement authentifié
 * @returns Promise avec les informations utilisateur
 * @throws ApiError en cas d'erreur
 */
export async function getMe(): Promise<User> {
  try {
    // Le client API fait déjà l'unwrapping, donc response.data contient directement
    // { id, email, username, role, created_at, ... }
    const response = await apiClient.get<User>('/auth/me');
    return response.data;
  } catch (error) {
    const apiError = parseApiError(error);
    throw apiError;
  }
}

/**
 * Auth API Service Object
 * Action 6.1.1.5: Exports unified authApi object with all auth operations
 */
import {
  AuthResponse,
  CheckUsernameRequest,
  CheckUsernameResponse,
  MessageResponse,
  RefreshResponse,
  RequestPasswordResetRequest,
  ResendVerificationRequest,
  ResetPasswordRequest,
  VerifyEmailRequest,
  type OAuthProvidersResponse,
  type TwoFactorSetupResponse,
  type TwoFactorStatusResponse,
} from '@/features/auth/types/index';
import { env } from '@/config/env';

export const authApi = {
  /**
   * Login with credentials (includes token storage)
   */
  login,

  /**
   * Register new user (includes token storage)
   */
  register,

  /**
   * Logout user (includes token cleanup)
   */
  logout,

  /**
   * Get current authenticated user
   */
  getMe,

  /**
   * Refresh access token
   */
  refresh: async (refreshToken: string): Promise<RefreshResponse> => {
    const { data } = await apiClient.post<RefreshResponse>('/auth/refresh', {
      refresh_token: refreshToken,
    });
    return data;
  },

  /**
   * Verify email with token
   */
  verifyEmail: async (
    request: VerifyEmailRequest,
  ): Promise<MessageResponse> => {
    const { data } = await apiClient.post<MessageResponse>(
      '/auth/verify-email',
      undefined,
      {
        params: { token: request.token },
      },
    );
    return data;
  },

  /**
   * Resend verification email
   */
  resendVerification: async (
    request: ResendVerificationRequest,
  ): Promise<MessageResponse> => {
    const { data } = await apiClient.post<MessageResponse>(
      '/auth/resend-verification',
      request,
    );
    return data;
  },

  /**
   * Check username availability
   */
  checkUsername: async (
    request: CheckUsernameRequest,
  ): Promise<CheckUsernameResponse> => {
    const { data } = await apiClient.get<CheckUsernameResponse>(
      '/auth/check-username',
      {
        params: { username: request.username },
      },
    );
    return data;
  },

  /**
   * Request password reset
   */
  requestPasswordReset: async (
    request: RequestPasswordResetRequest,
  ): Promise<MessageResponse> => {
    const { data } = await apiClient.post<MessageResponse>(
      '/auth/password/reset-request',
      request,
    );
    return data;
  },

  /**
   * Reset password with token
   */
  resetPassword: async (
    request: ResetPasswordRequest,
  ): Promise<MessageResponse> => {
    const { data } = await apiClient.post<MessageResponse>(
      '/auth/password/reset',
      request,
    );
    return data;
  },

  /**
   * Get OAuth providers
   */
  getOAuthProviders: async (): Promise<OAuthProvidersResponse> => {
    const { data } = await apiClient.get<OAuthProvidersResponse>(
      '/auth/oauth/providers',
    );
    return data;
  },

  /**
   * Initiate OAuth flow
   */
  initiateOAuth: (provider: string): void => {
    // Redirect browser to backend OAuth initiation endpoint
    window.location.href = `${env.API_URL}/auth/oauth/${provider}`;
  },

  /**
   * Setup 2FA
   */
  setup2FA: async (): Promise<TwoFactorSetupResponse> => {
    const { data } = await apiClient.post<TwoFactorSetupResponse>(
      '/auth/2fa/setup',
    );
    return data;
  },

  /**
   * Verify 2FA code
   */
  verify2FA: async (code: string): Promise<MessageResponse> => {
    const { data } = await apiClient.post<MessageResponse>('/auth/2fa/verify', {
      code,
    });
    return data;
  },

  /**
   * Disable 2FA
   */
  disable2FA: async (code: string): Promise<MessageResponse> => {
    const { data } = await apiClient.post<MessageResponse>(
      '/auth/2fa/disable',
      { code },
    );
    return data;
  },

  /**
   * Get 2FA status
   */
  get2FAStatus: async (): Promise<TwoFactorStatusResponse> => {
    const { data } = await apiClient.get<TwoFactorStatusResponse>(
      '/auth/2fa/status',
    );
    return data;
  },
};

// Re-export types for convenience
export type {
  LoginRequest,
  RegisterRequest,
  LoginResponse,
  RegisterResponse,
};
