import { useToast } from '@/hooks/useToast';
import { useUserStore } from '@/store';
import apiService from '@/api';
import { useTranslation } from 'react-i18next';
import i18n from '@/i18n';
import type { UserInfo } from '@/c-types';
import { useTracking } from '@/c-common/hooks/useTracking';

interface ApiResponse {
  code: number;
  data?: any;
  message?: string;
  msg?: string;
}

interface LoginResponse extends ApiResponse {
  data?: {
    userInfo: UserInfo;
    token: string;
  };
}

type ApiError = Error & {
  code?: number;
};

interface UseAuthOptions {
  onSuccess?: (userInfo: UserInfo) => void;
  onError?: (error: any) => void;
  loginContext?: string;
  courseId?: string;
}

export function useAuth(options: UseAuthOptions = {}) {
  const { toast } = useToast();
  const { login, logout } = useUserStore();
  const { t } = useTranslation();
  const { trackEvent } = useTracking();

  const buildApiError = (response: ApiResponse): ApiError => {
    const error = new Error(
      response.message || response.msg || t('common.core.networkError'),
    ) as ApiError;
    error.code = response.code;
    return error;
  };

  // Generic wrapper for API calls with automatic token refresh on expiration
  const callWithTokenRefresh = async <T extends ApiResponse>(
    apiCall: () => Promise<T>,
    hasRetried = false,
  ): Promise<T> => {
    const buildTokenRetryError = (message?: string) => {
      const error = new Error(
        message || t('module.auth.failed') || t('common.core.networkError'),
      ) as Error & { code?: number };
      error.code = 1005;
      return error;
    };

    const tokenBefore = useUserStore.getState().getToken?.() || '';
    try {
      const response = await apiCall();

      // Handle token expiration
      if (response.code === 1005) {
        if (!hasRetried) {
          const tokenAfter = useUserStore.getState().getToken?.() || '';
          // Request layer usually handles auth recovery. Only run local recovery
          // if token did not change (recovery likely did not happen yet).
          if (tokenAfter === tokenBefore) {
            await logout(false);
          }
          // Retry the API call once with the new guest token
          return await callWithTokenRefresh(apiCall, true);
        }
        throw buildTokenRetryError(response.message || response.msg);
      }

      return response;
    } catch (error: any) {
      if (error?.code === 1005 && !hasRetried) {
        const tokenAfter = useUserStore.getState().getToken?.() || '';
        if (tokenAfter === tokenBefore) {
          await logout(false);
        }
        return await callWithTokenRefresh(apiCall, true);
      }
      if (error?.code === 1005 && hasRetried) {
        throw buildTokenRetryError(error?.message);
      }
      throw error;
    }
  };

  // Handle common login errors
  const handleLoginError = (
    code: number,
    message?: string,
    context?: 'email' | 'sms',
  ) => {
    // Skip token expiration as it's handled by retry logic
    if (code === 1005) return;

    const title = t('module.auth.failed');
    let description: string;

    switch (code) {
      case 1001:
        description = t('module.auth.credentialError');
        break;
      case 1003:
        // For SMS context, 1003 means OTP expired; for email context, it means wrong credentials
        description =
          context === 'sms'
            ? t('module.auth.otpExpired')
            : t('module.auth.credentialError');
        break;
      case 1013:
        description = t('module.auth.otpExpired');
        break;
      case 1014:
        description = t('module.auth.otpInvalid');
        break;
      default:
        description = message || t('common.core.networkError');
    }

    toast({
      title,
      description,
      variant: 'destructive',
    });
  };

  // Process login response
  const processLoginResponse = async (
    response: LoginResponse,
    loginMethod?: string,
  ) => {
    if (response.code === 0 && response.data) {
      toast({
        title: t('module.auth.success'),
      });
      await login(response.data.userInfo, response.data.token);
      options.onSuccess?.(response.data.userInfo);
      if (loginMethod) {
        trackEvent('learner_login_success', {
          user_id: response.data.userInfo?.user_id || '',
          login_method: loginMethod,
        });
      }
      return true;
    }
    return false;
  };

  // SMS verification login with automatic retry on token expiration
  const loginWithSmsCode = async (
    mobile: string,
    sms_code: string,
    language: string,
  ) => {
    try {
      const response = await callWithTokenRefresh(() =>
        apiService.smsLogin({
          mobile,
          sms_code,
          language,
          login_context: options.loginContext,
          course_id: options.courseId,
        }),
      );

      const success = await processLoginResponse(response, 'sms');
      if (!success) {
        handleLoginError(
          response.code,
          response.message || response.msg,
          'sms',
        );
      }

      return response;
    } catch (error: any) {
      toast({
        title: t('module.auth.failed'),
        description: error.message || t('common.core.networkError'),
        variant: 'destructive',
      });
      options.onError?.(error);
      throw error;
    }
  };

  // Send SMS verification code with automatic token refresh
  const sendSmsCode = async (mobile: string, captchaTicket: string) => {
    try {
      const response = await callWithTokenRefresh(() =>
        apiService.sendSmsCode({
          mobile,
          captcha_ticket: captchaTicket,
          language: i18n.language,
        }),
      );

      if (response.code !== 0) {
        throw buildApiError(response);
      }

      return response;
    } catch (error: any) {
      toast({
        title: t('module.auth.sendFailed'),
        description: error.message || t('common.core.networkError'),
        variant: 'destructive',
      });
      throw error;
    }
  };

  return {
    loginWithSmsCode,
    sendSmsCode,
    callWithTokenRefresh,
  };
}
