import axios, { InternalAxiosRequestConfig } from 'axios';
import { jwtDecode } from 'jwt-decode'; // Updated import
import DeviceInfo from 'react-native-device-info';
import Toast from 'react-native-toast-message';
import { store } from '../app/_layout';
import config from '../config/config';

// Type definitions
interface UserInfo {
  access_token: string;
  expires_in: number;
  refresh_token: string;
  [key: string]: any;
}

interface UserState {
  userInfo: UserInfo;
}

interface RootState {
  userInfo: UserState;
}

interface DecodedToken {
  exp: number;
  iat: number;
  jti: string;
  nbf: number;
  [key: string]: any;
}

interface AuthResponse {
  access_token?: string;
  expires_in?: number;
  refresh_token?: string;
  message?: string;
  [key: string]: any;
}

interface LoginCredentials {
  username: string;
  password: string;
}

// Global variables (consider moving to secure storage)
let username: string | null = null;
let password: string | null = null;

/**
 * Sets the authentication token for axios requests
 */
const setAuthentication = (token: string): void => {
  if (token) {
    axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
    axios.defaults.timeout = 240000;
  }
};

/**
 * Checks if user is logged in based on Redux state
 */
const isUserLoggedIn = (): boolean => {
  const state = store.getState() as RootState;
  const { userInfo } = state.userInfo;
  return userInfo && Object.keys(userInfo).length > 0;
};

/**
 * Checks if user has valid credentials
 */
const isLoggedIn = async (): Promise<LoginCredentials | {}> => {
  if (username && password) {
    return { username, password };
  } else {
    delete axios.defaults.headers.common['Authorization'];
    return {};
  }
};

/**
 * Logs out the user by removing authorization header
 */
const logout = (): void => {
  delete axios.defaults.headers.common['Authorization'];
};

/**
 * Refreshes the access token using refresh token
 */
const setRefreshedNewToken = async (): Promise<any> => {
  try {
    const state = store.getState() as RootState;
    const { access_token, expires_in, refresh_token } = state.userInfo.userInfo;

    const deviceRemoveFormData = new FormData();
    deviceRemoveFormData.append('token', access_token);
    
    // Get device ID safely
    const deviceId = await DeviceInfo.getUniqueId();
    deviceRemoveFormData.append('device', deviceId);

    const formData = new FormData();
    formData.append('client_id', config.client_id);
    formData.append('client_secret', config.client_secret);
    formData.append('grant_type', 'refresh_token');
    formData.append('refresh_token', refresh_token);

    // Remove existing authorization header
    delete axios.defaults.headers.common['Authorization'];
    
    const res = await axios.post<AuthResponse>(
      `${config.apiserver}/oauth/token?_format=json`,
      formData,
      {
        headers: {
          'Content-Type': 'multipart/form-data',
        },
      }
    );

    if (res.data.access_token) {
      setAuthentication(res.data.access_token);
      
      store.dispatch({
        type: 'AUTHENTICATION_SUCCESS',
        payload: res.data,
      });

      Toast.show({
        type: 'success',
        text1: 'Token Updated',
        text2: 'New access token updated. You can continue with any previously failed tasks.',
        position: 'bottom',
        visibilityTime: 30000,
      });
    }
    
    return res;
  } catch (error: any) {
    if (
      error.response?.data?.message === 'The refresh token is invalid.'
    ) {
      console.log('Refresh token invalid');
      
      logout();

      setTimeout(async () => {
        store.dispatch({
          type: 'AUTHENTICATION_FAILED',
          payload: {},
        });

        try {
          const deviceRemoveFormData = new FormData();
          const state = store.getState() as RootState;
          deviceRemoveFormData.append('token', state.userInfo.userInfo.access_token);
          
          const deviceId = await DeviceInfo.getUniqueId();
          deviceRemoveFormData.append('device', deviceId);

          await axios.post(
            `${config.apiserver}/api/v1/notifications/remove-device-token`,
            deviceRemoveFormData
          );
        } catch (deviceError) {
          console.log('Remove device token error:', deviceError);
        }
      }, 2000);

      Toast.show({
        type: 'error',
        text1: 'Session Expired',
        text2: 'User credentials were incorrect or session expired. Please login again.',
        position: 'bottom',
        visibilityTime: 30000,
      });
    } else {
      console.log('Error while fetching new access token:', error);
    }
    
    throw error;
  }
};

/**
 * Alternative method to refresh token using fetch
 */
const newRefreshToken = async (): Promise<AuthResponse | null> => {
  try {
    const state = store.getState() as RootState;
    const { access_token, expires_in, refresh_token } = state.userInfo.userInfo;
    
    const formData = new FormData();
    formData.append('client_id', config.client_id);
    formData.append('client_secret', config.client_secret);
    formData.append('grant_type', 'refresh_token');
    formData.append('refresh_token', refresh_token);

    const response = await fetch(`${config.apiserver}/oauth/token?_format=json`, {
      method: 'POST',
      headers: {
        'Content-Type': 'multipart/form-data',
      },
      body: formData,
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const data: AuthResponse = await response.json();
    return data;
  } catch (error) {
    console.error('Error in newRefreshToken:', error);
    return null;
  }
};

/**
 * Sets up axios interceptor to automatically refresh tokens
 */
const checkAndUpdateToken = (): void => {
  axios.interceptors.request.use(
    async (config: InternalAxiosRequestConfig) => {
      const authHeader = config.headers?.['Authorization'] as string | undefined;

      if (authHeader) {
        const token = authHeader.replace('Bearer ', '');

        try {
          const decoded = jwtDecode<DecodedToken>(token);
          const nowTimeStamp = Math.floor(Date.now() / 1000);

          if (nowTimeStamp >= decoded.exp) {
            try {
              const res = await newRefreshToken();

              if (res?.access_token) {
                setAuthentication(res.access_token);

                store.dispatch({
                  type: 'AUTHENTICATION_SUCCESS',
                  payload: res,
                });

                Toast.show({
                  type: 'success',
                  text1: 'Token Updated',
                  text2: 'Access token updated. You can continue with any previously failed tasks.',
                  position: 'bottom',
                  visibilityTime: 30000,
                });

                // Update the request with new token
                config.headers['Authorization'] = `Bearer ${res.access_token}`;
              } else if (res?.message === 'The refresh token is invalid.') {
                logout();

                store.dispatch({
                  type: 'AUTHENTICATION_FAILED',
                  payload: {},
                });

                Toast.show({
                  type: 'error',
                  text1: 'Session Expired',
                  text2: 'Please login again.',
                  position: 'bottom',
                  visibilityTime: 5000,
                });
              }
            } catch (tokenError) {
              console.error('Token refresh error:', tokenError);
            }
          }
        } catch (decodeError) {
          console.error('Token decode error:', decodeError);
        }
      }

      return config;
    },
    (error) => Promise.reject(error)
  );
};

export {
    checkAndUpdateToken, isUserLoggedIn, logout, setAuthentication, setRefreshedNewToken, type AuthResponse,
    type LoginCredentials, type UserInfo
};
