import React from 'react';
import {createContext, ReactNode, useContext, useState} from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
import moment from 'moment';
import {SdkService} from '@services/AffinidiSDKService';

const LOGIN_TOKEN = '@LOGIN_TOKEN';
const TOKEN_EXPIRES_IN = '@TOKEN_EXPIRES_IN';
const PASSWORD = '@PASSWORD';
const ENCRYPTED_SEED = '@ENCRYPTED_SEED';
const PHONE_NUMBER = '@PHONE_NUMBER';
const IS_RETURNING_USER = '@IS_RETURNING_USER';
export const PREV_READ_NOTIFICATIONS = '@PREV_READ_NOTIFICATIONS';
export const ACCESS_TOKEN = '@ACCESS_TOKEN';

interface IValueProps {
  accessToken: string | undefined | null;
  hasNewNotifications: boolean;
}

export interface IUserProps {
  user: IValueProps;
  sdk: SdkService | undefined;
  credentials: any[] | undefined;
  credentialRequests: any;
  setCredentials: (credentials: any[] | undefined) => void;
  setCredentialRequests: (credentials: any) => void;
  initiateSdk: (sdk: SdkService) => void;
  setUser: (data: IValueProps) => void;
  setLoginToken: (loginToken: string) => void;
  getLoginToken: () => Promise<string | null | undefined>;
  setAccessToken: (loginToken: string) => void;
  getAccessToken: () => Promise<string | null | undefined>;
  setTokenExpiresIn: (timestamp: number) => void;
  getTokenExpiresIn: () => Promise<string | undefined | null>;
  isTokenExpired: () => Promise<boolean>;
  setPassword: (password: string) => void;
  getPassword: () => Promise<string | undefined | null>;
  setEncryptedSeed: (encryptedSeed: string) => void;
  getEncryptedSeed: () => Promise<string | undefined | null>;
  setPhoneNumber: (phoneNumber: string) => void;
  getPhoneNumber: () => Promise<string | undefined | null>;
  isReturningUser: () => Promise<boolean>;
  logout: () => void;
}

const contextDefaultValues: IUserProps = {
  user: {accessToken: undefined, hasNewNotifications: false},
  sdk: undefined,
  credentials: undefined,
  credentialRequests: undefined,
  initiateSdk: (sdk: SdkService) => sdk,
  setUser: (data: IValueProps) => data,
  setLoginToken: async (loginToken: string) => loginToken,
  getLoginToken: async () => '',
  setAccessToken: async (loginToken: string) => loginToken,
  getAccessToken: async () => '',
  setTokenExpiresIn: async (timestamp: number) => timestamp,
  getTokenExpiresIn: async () => undefined,
  setPassword: async (password: string) => password,
  getPassword: async () => undefined,
  isTokenExpired: async () => true,
  setEncryptedSeed: async (encryptedSeed: string) => encryptedSeed,
  getEncryptedSeed: async () => undefined,
  setCredentials: (credentials: any[] | undefined) => credentials,
  setCredentialRequests: (credentials: any) => credentials,
  setPhoneNumber: async (phoneNumber: string) => phoneNumber,
  getPhoneNumber: async () => undefined,
  isReturningUser: async () => false,
  logout: async () => {},
};

const UserContext = createContext<IUserProps>(contextDefaultValues);

const setLocalValue = async (key: string, value: string) => {
  try {
    await AsyncStorage.setItem(key, value);
  } catch (error) {
    console.error(error);
  }
};

const getLocalValue = async (key: string) => {
  try {
    const value = await AsyncStorage.getItem(key);
    return value;
  } catch (error) {
    console.error(error);
  }
};

export const UserProvider = ({children}: {children: ReactNode}) => {
  const [user, setUser] = useState(contextDefaultValues.user);
  const [sdk, setSdk] = useState(contextDefaultValues.sdk);
  const [credentials, setCredentials] = useState(
    contextDefaultValues.credentials,
  );
  const [credentialRequests, setCredentialRequests] = useState(
    contextDefaultValues.credentialRequests,
  );

  const setLoginToken = async (loginToken: string) => {
    await setLocalValue(LOGIN_TOKEN, loginToken);
    await setLocalValue(IS_RETURNING_USER, 'TRUE');
  };

  const getLoginToken = async () => await getLocalValue(LOGIN_TOKEN);

  const setAccessToken = React.useCallback(
    async (accessToken: string) => {
      setUser({...user, accessToken});
      await setLocalValue(ACCESS_TOKEN, accessToken);
    },
    [user],
  );

  const getAccessToken = React.useCallback(async () => {
    const accessToken = await getLocalValue(ACCESS_TOKEN);

    if (!user?.accessToken && !!accessToken) {
      setUser({...user, accessToken});
    }
    return accessToken;
  }, [user]);

  const setTokenExpiresIn = async (time: number) =>
    await setLocalValue(TOKEN_EXPIRES_IN, time?.toString());

  const getTokenExpiresIn = async () => await getLocalValue(TOKEN_EXPIRES_IN);

  const isTokenExpired = React.useCallback(async () => {
    const tokenExpiresInValue = await getTokenExpiresIn();

    if (!tokenExpiresInValue) {
      return true;
    }

    // return moment().utc().valueOf() > Number(tokenExpiresInValue);

    return moment().unix() > Number(tokenExpiresInValue);
  }, []);

  const setPassword = async (password: string) =>
    await setLocalValue(PASSWORD, password);

  const getPassword = async () => await getLocalValue(PASSWORD);

  const setEncryptedSeed = async (encryptedSeed: string) =>
    await setLocalValue(ENCRYPTED_SEED, encryptedSeed);

  const getEncryptedSeed = async () => await getLocalValue(ENCRYPTED_SEED);

  const initiateSdk = (v: SdkService) => setSdk(v);

  const logout = async () => {
    await AsyncStorage.multiRemove([
      LOGIN_TOKEN,
      ACCESS_TOKEN,
      TOKEN_EXPIRES_IN,
      PASSWORD,
      PHONE_NUMBER,
      ENCRYPTED_SEED,
    ]);
    setUser(contextDefaultValues.user);
    setSdk(contextDefaultValues.sdk);
    setCredentials(contextDefaultValues.credentials);
  };

  const setPhoneNumber = async (phoneNumber: string) =>
    await setLocalValue(PHONE_NUMBER, phoneNumber);

  const getPhoneNumber = async () => await getLocalValue(PHONE_NUMBER);

  const isReturningUser = async () => {
    const value = await getLocalValue(IS_RETURNING_USER);
    return value === 'TRUE' ? true : false;
  };

  return (
    <UserContext.Provider
      value={React.useMemo(
        () => ({
          user,
          sdk,
          setUser,
          setLoginToken,
          getLoginToken,
          setAccessToken,
          getAccessToken,
          setTokenExpiresIn,
          getTokenExpiresIn,
          isTokenExpired,
          setPassword,
          getPassword,
          setEncryptedSeed,
          getEncryptedSeed,
          initiateSdk,
          credentials,
          credentialRequests,
          setCredentialRequests,
          setCredentials,
          setPhoneNumber,
          getPhoneNumber,
          isReturningUser,
          logout,
        }),
        [
          user,
          sdk,
          credentials,
          getAccessToken,
          isTokenExpired,
          setAccessToken,
          credentialRequests,
        ],
      )}>
      {children}
    </UserContext.Provider>
  );
};

export default () => useContext(UserContext);
