import React, { useCallback, useEffect, useMemo, useState } from 'react';
import {
  CurrencyDenom,
  DecCoin,
  DelegationWithEverything,
  Fee,
  FeeDetails,
  TransactionExecuteResult,
} from '@nymproject/types';
import { DelegationContext } from '../delegations';

import { mockSleep } from './utils';
import { TPoolOption } from '../../components';

const SLEEP_MS = 1000;

let mockDelegations: DelegationWithEverything[] = [
  {
    mix_id: 1234,
    node_identity: 'FiojKW7oY9WQmLCiYAsCA21tpowZHS6zcUoyYm319p6Z',
    delegated_on_iso_datetime: new Date(2021, 1, 1).toDateString(),
    unclaimed_rewards: { amount: '0.05', denom: 'nym' },
    amount: { amount: '10', denom: 'nym' },
    owner: '',
    block_height: BigInt(100),
    cost_params: {
      profit_margin_percent: '0.04',
      interval_operating_cost: {
        amount: '20',
        denom: 'nym',
      },
    },
    stake_saturation: '0.2',
    avg_uptime_percent: 0.5,
    accumulated_by_delegates: { amount: '0', denom: 'nym' },
    accumulated_by_operator: { amount: '0', denom: 'nym' },
    uses_vesting_contract_tokens: false,
    pending_events: [],
    mixnode_is_unbonding: false,
    historical_node_identity: null,
    errors: null,
  },
  {
    mix_id: 5678,
    node_identity: 'DT8S942S8AQs2zKHS9SVo1GyHmuca3pfL2uLhLksJ3D8',
    unclaimed_rewards: { amount: '0.1', denom: 'nym' },
    amount: { amount: '100', denom: 'nym' },
    delegated_on_iso_datetime: new Date(2021, 1, 2).toDateString(),
    owner: '',
    block_height: BigInt(4000),
    stake_saturation: '0.5',
    avg_uptime_percent: 0.1,
    cost_params: {
      profit_margin_percent: '0.04',
      interval_operating_cost: {
        amount: '60',
        denom: 'nym',
      },
    },
    accumulated_by_delegates: { amount: '0', denom: 'nym' },
    accumulated_by_operator: { amount: '0', denom: 'nym' },
    uses_vesting_contract_tokens: true,
    pending_events: [],
    mixnode_is_unbonding: false,
    historical_node_identity: null,
    errors: null,
  },
];

export const MockDelegationContextProvider: FCWithChildren = ({ children }) => {
  const [trigger, setTrigger] = useState<Date>(new Date());
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string>();
  const [delegations, setDelegations] = useState<undefined | DelegationWithEverything[]>();
  const [totalDelegations, setTotalDelegations] = useState<undefined | string>();
  const [totalRewards, setTotalRewards] = useState<undefined | string>();
  const [totalDelegationsAndRewards, setTotalDelegationsAndRewards] = useState<undefined | string>();
  const [lastUpdatedAtMs, setLastUpdatedAtMs] = useState(0);
  const [delegationItemErrors, setDelegationItemErrors] = useState<{ nodeId: string; errors: string }>();

  const triggerStateUpdate = () => setTrigger(new Date());

  const getDelegations = async (): Promise<DelegationWithEverything[]> =>
    mockDelegations.sort((a, b) => a.node_identity.localeCompare(b.node_identity));

  const recalculate = async () => {
    const newDelegations = await getDelegations();
    const newTotalDelegations = `${newDelegations.length * 100} NYM`;
    const rewardsSum = newDelegations.reduce((acc, d) => {
      const n = parseFloat(d.unclaimed_rewards?.amount ?? '0');
      return acc + (Number.isFinite(n) ? n : 0);
    }, 0);
    const newTotalRewards = `${rewardsSum} nym`;
    setDelegations(newDelegations);
    setTotalDelegations(newTotalDelegations);
    setTotalRewards(newTotalRewards);
    setTotalDelegationsAndRewards(`${newTotalDelegations} + ${newTotalRewards}`);
    setLastUpdatedAtMs(Date.now());
  };

  const addDelegation = async (
    data: { mix_id: number; amount: DecCoin },
    _tokenPool: TPoolOption,
    _fee?: FeeDetails,
  ): Promise<TransactionExecuteResult> => {
    await mockSleep(SLEEP_MS);
    await recalculate();
    triggerStateUpdate();

    setTimeout(async () => {
      mockDelegations = mockDelegations.map((d) => {
        if (d.mix_id === data.mix_id) {
          return { ...d, isPending: undefined };
        }
        return d;
      });
      await recalculate();
      triggerStateUpdate();
    }, 3000);

    return {
      logs_json: '',
      msg_responses_json: '',
      gas_info: {
        gas_wanted: { gas_units: BigInt(1) },
        gas_used: { gas_units: BigInt(1) },
      },
      transaction_hash: '55303CD4B91FAC4C2715E40EBB52BB3B92829D9431B3A279D37B5CC58432E354',
      fee: { amount: '1', denom: 'nym' },
    };
  };

  const undelegate = async (mix_id: number, _fee?: Fee): Promise<TransactionExecuteResult> => {
    await mockSleep(SLEEP_MS);
    mockDelegations = mockDelegations.map((d) => {
      if (d.mix_id === mix_id) {
        return { ...d, isPending: { blockHeight: 5678, actionType: 'undelegate' } };
      }
      return d;
    });
    await recalculate();
    triggerStateUpdate();

    setTimeout(async () => {
      mockDelegations = mockDelegations.filter((d) => d.mix_id !== mix_id);
      await recalculate();
      triggerStateUpdate();
    }, 3000);

    return {
      logs_json: '',
      msg_responses_json: '',
      transaction_hash: '',
      gas_info: {
        gas_wanted: { gas_units: BigInt(1) },
        gas_used: { gas_units: BigInt(1) },
      },
      fee: { amount: '1', denom: 'nym' as CurrencyDenom },
    };
  };

  const undelegateVesting = async (_mix_id: number): Promise<TransactionExecuteResult> => ({
    msg_responses_json: '',
    logs_json: '',
    transaction_hash: '',
    gas_info: {
      gas_wanted: { gas_units: BigInt(1) },
      gas_used: { gas_units: BigInt(1) },
    },
    fee: { amount: '1', denom: 'nym' as CurrencyDenom },
  });

  const resetState = () => {
    setIsLoading(true);
    setError(undefined);
    setTotalDelegations(undefined);
    setTotalRewards(undefined);
    setTotalDelegationsAndRewards(undefined);
    setLastUpdatedAtMs(0);
    setDelegations([]);
  };

  const refresh = useCallback(async () => {
    resetState();
    setTimeout(async () => {
      try {
        await mockSleep(SLEEP_MS);
        await recalculate();
      } catch (e) {
        setError((e as Error).message);
      }
      setIsLoading(false);
    }, 2000);
  }, []);

  useEffect(() => {
    resetState();
    refresh();
  }, []);

  const memoizedValue = useMemo(
    () => ({
      delegationItemErrors,
      setDelegationItemErrors,
      isLoading,
      isFetching: isLoading,
      isError: Boolean(error),
      lastUpdatedAtMs,
      delegations,
      pendingDelegations: [],
      totalDelegations,
      totalRewards,
      totalDelegationsAndRewards,
      refresh,
      addDelegation,
      undelegate,
      undelegateVesting,
    }),
    [
      isLoading,
      error,
      delegations,
      totalDelegations,
      totalRewards,
      totalDelegationsAndRewards,
      lastUpdatedAtMs,
      refresh,
      addDelegation,
      undelegate,
      undelegateVesting,
      delegationItemErrors,
      trigger,
    ],
  );

  return <DelegationContext.Provider value={memoizedValue}>{children}</DelegationContext.Provider>;
};
