import { AnyAction, configureStore, Middleware } from '@reduxjs/toolkit';
import axios, { AxiosResponse } from 'axios';
import { jwtDecode } from 'jwt-decode';
import moment from 'moment';
import Toast from 'react-native-toast-message';
import { createTransform, Persistor, persistReducer, persistStore } from 'redux-persist';
import FilesystemStorage from 'redux-persist-filesystem-storage';
import { thunk, ThunkDispatch } from 'redux-thunk';
import config from '../config/config';
import { setAuthentication } from '../utils/authentication';
import rootReducer, { RootState } from './reducers'; // Import RootState from reducers

// Type definitions
interface DecodedToken {
  exp: number;
  iat: number;
  [key: string]: any;
}

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

interface UserInfoState {
  userInfo: UserInfo;
  refreshTokenPromise?: Promise<any>;
  isRefreshing?: boolean;
  error?: string;
}

// Remove the manual RootState interface - use the one from reducers instead

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

interface RefreshTokenResult {
  status: 'success' | 'failed';
  data: AxiosResponse<AuthResponse> | string;
}

type AppDispatch = ThunkDispatch<RootState, unknown, AnyAction>;

// Transform to handle non-serializable data
const blacklistTransform = createTransform<RootState['userInfo'], RootState['userInfo']>(
  // inbound: before storing
  (inboundState, key) => {
    if (key === 'userInfo') {
      const { refreshTokenPromise, ...serializedState } = inboundState;
      return serializedState;
    }
    return inboundState;
  },
  // outbound: when rehydrating
  (outboundState) => {
    return outboundState;
  },
  // Only apply to userInfo slice
  { whitelist: ['userInfo'] }
);

const persistConfig = {
  key: 'root',
  transforms: [blacklistTransform],
  storage: FilesystemStorage,
  whitelist: [
    'userInfo',
    'permissions',
    'incidentReport',
    'auditFormData',
    'auditSubmitData',
    'inspectionFormData',
    'inspectionSubmitData',
    'checkListFormData',
    'checkListSubmissions',
    'checklistSubmitData',
    'draftAnswerData',
    'initData',
    'permitFormData',
    'permitSubmitData',
    'notificationData',
    'failedJobsData',
    'historyData'
  ],
};

/**
 * Checks if the access token is expired (with 60 second buffer)
 */
const isAccessTokenExpired = (token: string): boolean => {
  try {
    const decoded = jwtDecode<DecodedToken>(token);
    const { exp } = decoded;
    const expMinus60sec = moment.unix(exp).subtract(60, 'seconds').unix();
    const nowTimeStamp = Math.round(Date.now() / 1000);
    return nowTimeStamp > expMinus60sec;
  } catch (error) {
    console.error('Error decoding token:', error);
    return true;
  }
};

/**
 * Refreshes the authentication token
 */
function refreshToken(
  dispatch: AppDispatch,
  state: RootState
): Promise<RefreshTokenResult> {
  dispatch({
    type: 'AUTHENTICATION_REFRESH',
    payload: true,
  });

  const { userInfo: { refresh_token } } = state.userInfo;
  
  if (!refresh_token) {
    const error = new Error('No refresh token available');
    dispatch({
      type: 'AUTHENTICATION_FAILED',
      payload: 'No refresh token available',
    });
    return Promise.reject(error);
  }

  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);
  
  // Clear existing authorization
  delete axios.defaults.headers.common['Authorization'];

  const refreshTokenPromise = axios
    .post<AuthResponse>(`${config.apiserver}/oauth/token?_format=json`, formData, {
      headers: {
        'Content-Type': 'multipart/form-data',
      },
    })
    .then((res: AxiosResponse<AuthResponse>) => {
      if (res.data.access_token) {
        setAuthentication(res.data.access_token);
        
        dispatch({
          type: 'AUTHENTICATION_SUCCESS',
          payload: res.data,
        });
        
        dispatch({
          type: 'AUTHENTICATION_REFRESH',
          payload: false,
        });

        Toast.show({
          type: 'success',
          text1: 'Access token updated',
          position: 'bottom',
          visibilityTime: 3000,
        });

        return Promise.resolve<RefreshTokenResult>({
          status: 'success',
          data: res,
        });
      } else {
        throw new Error('No access token in response');
      }
    })
    .catch((error) => {
      console.error('Refresh token API error:', error);
      
      dispatch({
        type: 'AUTHENTICATION_REFRESH',
        payload: false,
      });
      
      dispatch({
        type: 'AUTHENTICATION_FAILED',
        payload: 'Please login to continue...',
      });

      Toast.show({
        type: 'error',
        text1: 'Session expired',
        text2: 'Please login to continue',
        position: 'bottom',
        visibilityTime: 5000,
      });

      return Promise.reject<RefreshTokenResult>({
        status: 'failed',
        data: error.message || 'Token refresh failed',
      });
    });

  dispatch({
    type: 'REFRESH_TOKEN_PROMISE',
    refreshTokenPromise,
  });

  return refreshTokenPromise;
}

/**
 * JWT middleware to handle token expiration and refresh
 */
const jwtMiddleware: Middleware<{}, RootState, AppDispatch> = 
  ({ dispatch, getState }) =>
  (next) =>
  (action: any) => { // <-- use 'any' here to satisfy redux-persist
    const shouldCheckToken = 
      typeof action === 'function' || 
      (action.type && action.type.includes('_REQUEST'));

    if (shouldCheckToken) {
      const state = getState();
      const userInfoState = state.userInfo;
      
      if (
        userInfoState?.userInfo?.access_token &&
        userInfoState?.userInfo?.refresh_token
      ) {
        const { access_token } = userInfoState.userInfo;
        
        if (isAccessTokenExpired(access_token)) {
          if (!userInfoState.refreshTokenPromise) {
            return refreshToken(dispatch, state).then(() => next(action));
          } else {
            return userInfoState.refreshTokenPromise.then(() => next(action));
          }
        } else {
          setAuthentication(access_token);
        }
      }
    }

    return next(action);
  };

// persisted reducer - cast to fix action type compatibility
const persistedReducer = persistReducer<RootState, AnyAction>(persistConfig, rootReducer as any);

export default function createAppStore(): { store: ReturnType<typeof configureStore>; persistor: Persistor } {
  const store = configureStore({
    reducer: persistedReducer,
    middleware: (getDefaultMiddleware) =>
      getDefaultMiddleware({
        serializableCheck: {
          ignoredActions: [
            'persist/PERSIST',
            'persist/REHYDRATE',
            'persist/PAUSE',
            'persist/PURGE',
            'persist/REGISTER',
            'REFRESH_TOKEN_PROMISE',
          ],
          ignoredPaths: ['userInfo.refreshTokenPromise'],
        },
        immutableCheck: {
          ignoredPaths: ['userInfo.refreshTokenPromise'],
        },
      }).concat(jwtMiddleware, thunk),
    devTools: __DEV__,
  });

  const persistor = persistStore(store);

  return { store, persistor };
}

// Export types for use in other files
export type { AppDispatch, AuthResponse, UserInfo, UserInfoState };
// Re-export RootState from reducers
  export type { RootState };

// Export store instance type
export type AppStore = ReturnType<typeof createAppStore>['store'];