import { Polymesh } from '@polymathnetwork/polymesh-sdk';
import {
  AccountBalance,
  Identity,
  Permissions,
} from '@polymathnetwork/polymesh-sdk/types';
import { createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit';

export type PolymeshState = {
  sdk: Polymesh | null;
  wallet: any; // @TODO: add wallet definition types
  currentAccount?: string;
  currentIdentity?: Identity;
  isDidValid?: boolean;
  didIssuer?: Identity;
  didExpiry?: Date | null;
  hasUid?: boolean;
  permissions?: Permissions | any;
  inFlight?: boolean;
  chainProperties?: any;
  accountBalance?: AccountBalance;
};

const initialState: PolymeshState = {
  sdk: null,
  wallet: null,
};

export const polymeshSlice = createSlice({
  name: 'polymesh',
  initialState,
  reducers: {
    setSdk(state, action: PayloadAction<any>) {
      state.sdk = action.payload;
    },
    setWallet(state, action: PayloadAction<any>) {
      state.wallet = action.payload;
    },
    setCurrentAccount(state, action: PayloadAction<string>) {
      state.currentAccount = action.payload;
    },
    setHasUid(state, action: PayloadAction<boolean>) {
      state.hasUid = action.payload;
    },
    setCurrentIdentity(state, action: PayloadAction<Identity>) {
      state.currentIdentity = action.payload;
    },
    setIsDidValid(state, action: PayloadAction<boolean>) {
      state.isDidValid = action.payload;
    },
    setDidIssuer(state, action: PayloadAction<Identity>) {
      state.didIssuer = action.payload;
    },
    setDidExpiry(state, action: PayloadAction<Date | null | undefined>) {
      state.didExpiry = action.payload;
    },
    setPermissions(state, action: PayloadAction<Permissions | any>) {
      state.permissions = action.payload;
    },
    setIsItn(state, action: PayloadAction<boolean>) {
      state.inFlight = action.payload;
    },
    setChainProperties(state, action: PayloadAction<any>) {
      state.chainProperties = action.payload;
    },
    setAccountBalance(state, action: PayloadAction<AccountBalance>) {
      state.accountBalance = action.payload;
    },
  },
});

export const polymeshReducer = polymeshSlice.reducer;

export const polymeshActions = polymeshSlice.actions;

/**
 * Factory function to return state selectors from provided polymesh selector
 * @param polymeshSelector selector function for polymesh state
 */
export function getPolymeshStateSelectors<RootState>(
  polymeshSelector: (rootState: RootState) => PolymeshState,
) {
  const walletSelector = createSelector(polymeshSelector, (polymesh) => {
    if (polymesh.wallet === null) throw new Error('Polymesh wallet is not set');

    return polymesh.wallet;
  });

  const sdkSelector = createSelector(polymeshSelector, (polymesh) => {
    if (polymesh.sdk === null) throw new Error('Polymesh SDK not set');

    return polymesh.sdk;
  });

  const currentAccountSelector = createSelector(
    polymeshSelector,
    (polymesh) => polymesh.currentAccount,
  );

  const currentIdentitySelector = createSelector(
    polymeshSelector,
    (polymesh) => polymesh.currentIdentity,
  );

  const isDidValidSelector = createSelector(
    polymeshSelector,
    (polymesh) => polymesh.isDidValid,
  );

  const didIssuerSelector = createSelector(
    polymeshSelector,
    (polymesh) => polymesh.didIssuer,
  );

  const didExpirySelector = createSelector(
    polymeshSelector,
    (polymesh) => polymesh.didExpiry,
  );

  const hasUidSelector = createSelector(
    polymeshSelector,
    (polymesh) => polymesh.hasUid,
  );

  const permissionsSelector = createSelector(
    polymeshSelector,
    (polymesh) => polymesh.permissions,
  );

  const inFlightSelector = createSelector(
    polymeshSelector,
    (polymesh) => polymesh.inFlight,
  );

  const chainPropertiesSelector = createSelector(
    polymeshSelector,
    (polymesh) => polymesh.chainProperties,
  );

  const accountBalanceSelector = createSelector(
    polymeshSelector,
    (polymesh) => polymesh.accountBalance,
  );

  const availableBalanceSelector = createSelector(
    accountBalanceSelector,
    (balance) => balance?.free.minus(balance.locked),
  );

  return {
    polymeshSelector,
    walletSelector,
    sdkSelector,
    currentAccountSelector,
    currentIdentitySelector,
    isDidValidSelector,
    didIssuerSelector,
    didExpirySelector,
    hasUidSelector,
    permissionsSelector,
    inFlightSelector,
    chainPropertiesSelector,
    accountBalanceSelector,
    availableBalanceSelector,
  };
}
