import debounce from 'lodash/debounce';
import { useMemo, useState } from 'react';
import BigNumber from 'bignumber.js';
import type { ColDef, ValueFormatterFunc } from 'ag-grid-community';
import {
  useAssetsMapProvider,
  type AssetFieldsFragment,
} from '@vegaprotocol/assets';
import {
  addDecimalsFormatNumberQuantum,
  formatNumberPercentage,
} from '@vegaprotocol/utils';
import { AgGrid, StackedCell } from '@vegaprotocol/datagrid';
import {
  TradingButton,
  VegaIcon,
  VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import {
  useRewardsHistoryQuery,
  type RewardsHistoryQuery,
} from './__generated__/Rewards';
import { useRewardsRowData } from './use-reward-row-data';
import { useT } from '../../lib/use-t';

export const RewardsHistoryContainer = ({
  epoch,
  pubKey,
}: {
  pubKey: string | null;
  epoch: number;
}) => {
  const [epochVariables, setEpochVariables] = useState(() => ({
    from: epoch - 1,
    to: epoch,
  }));

  const { data: assets } = useAssetsMapProvider();

  // No need to specify the fromEpoch as it will by default give you the last
  const { refetch, data, loading } = useRewardsHistoryQuery({
    variables: {
      partyId: pubKey || '',
      fromEpoch: epochVariables.from,
      toEpoch: epochVariables.to,
    },
  });

  const debouncedRefetch = useMemo(
    () => debounce((variables) => refetch(variables), 800),
    [refetch]
  );

  const handleEpochChange = (incoming: { from: number; to: number }) => {
    if (!Number.isInteger(incoming.from) || !Number.isInteger(incoming.to)) {
      return;
    }

    if (incoming.from > incoming.to) {
      return;
    }

    // Must be at least the first epoch
    if (incoming.from < 0 || incoming.to < 0) {
      return;
    }

    if (incoming.from > epoch || incoming.to > epoch) {
      return;
    }

    setEpochVariables({
      from: incoming.from,
      to: Math.min(incoming.to, epoch),
    });
    debouncedRefetch({
      partyId: pubKey || '',
      fromEpoch: incoming.from,
      toEpoch: incoming.to,
    });
  };

  return (
    <RewardHistoryTable
      pubKey={pubKey}
      epochRewardSummaries={data?.epochRewardSummaries}
      partyRewards={data?.party?.rewardsConnection}
      onEpochChange={handleEpochChange}
      epoch={epoch}
      epochVariables={epochVariables}
      assets={assets}
      loading={loading}
    />
  );
};

const defaultColDef = {
  flex: 1,
  resizable: true,
  sortable: true,
};

interface RewardRow {
  asset: AssetFieldsFragment;
  staking: number;
  priceTaking: number;
  priceMaking: number;
  liquidityProvision: number;
  marketCreation: number;
  averagePosition: number;
  relativeReturns: number;
  returnsVolatility: number;
  validatorRanking: number;
  total: number;
}

export type PartyRewardsConnection = NonNullable<
  RewardsHistoryQuery['party']
>['rewardsConnection'];

export const RewardHistoryTable = ({
  epochRewardSummaries,
  partyRewards,
  assets,
  pubKey,
  epochVariables,
  epoch,
  onEpochChange,
  loading,
}: {
  epochRewardSummaries: RewardsHistoryQuery['epochRewardSummaries'];
  partyRewards: PartyRewardsConnection;
  assets: Record<string, AssetFieldsFragment> | null;
  pubKey: string | null;
  epoch: number;
  epochVariables: {
    from: number;
    to: number;
  };
  onEpochChange: (epochVariables: { from: number; to: number }) => void;
  loading: boolean;
}) => {
  const t = useT();
  const [isParty, setIsParty] = useState(false);

  const rowData = useRewardsRowData({
    epochRewardSummaries,
    partyRewards,
    assets,
    partyId: isParty ? pubKey : null,
  });

  const columnDefs = useMemo<ColDef<RewardRow>[]>(() => {
    const rewardValueFormatter: ValueFormatterFunc<RewardRow> = ({
      data,
      value,
    }) => {
      if (!value || !data) {
        return '-';
      }
      return addDecimalsFormatNumberQuantum(
        value,
        data.asset.decimals,
        data.asset.quantum
      );
    };

    const rewardCellRenderer = ({
      data,
      value,
      valueFormatted,
    }: {
      data: RewardRow;
      value: number;
      valueFormatted: string;
    }) => {
      if (!value || value <= 0 || !data) {
        return <span className="text-muted">-</span>;
      }

      const pctOfTotal = new BigNumber(value).dividedBy(data.total).times(100);

      return (
        <StackedCell
          primary={valueFormatted}
          secondary={formatNumberPercentage(pctOfTotal, 2)}
        />
      );
    };

    const colDefs: ColDef[] = [
      {
        field: 'asset.symbol',
        cellRenderer: ({ value, data }: { value: string; data: RewardRow }) => {
          if (!value || !data) return <span>-</span>;
          return <StackedCell primary={value} secondary={data.asset.name} />;
        },
        sort: 'desc',
      },
      {
        field: 'staking',
        valueFormatter: rewardValueFormatter,
        cellRenderer: rewardCellRenderer,
      },
      {
        field: 'priceTaking',
        valueFormatter: rewardValueFormatter,
        cellRenderer: rewardCellRenderer,
      },
      {
        field: 'priceMaking',
        valueFormatter: rewardValueFormatter,
        cellRenderer: rewardCellRenderer,
      },
      {
        field: 'liquidityProvision',
        valueFormatter: rewardValueFormatter,
        cellRenderer: rewardCellRenderer,
      },
      {
        field: 'marketCreation',
        valueFormatter: rewardValueFormatter,
        cellRenderer: rewardCellRenderer,
      },
      {
        field: 'averagePosition',
        valueFormatter: rewardValueFormatter,
        cellRenderer: rewardCellRenderer,
      },
      {
        field: 'relativeReturns',
        valueFormatter: rewardValueFormatter,
        cellRenderer: rewardCellRenderer,
      },
      {
        field: 'returnsVolatility',
        valueFormatter: rewardValueFormatter,
        cellRenderer: rewardCellRenderer,
      },
      {
        field: 'validatorRanking',
        valueFormatter: rewardValueFormatter,
        cellRenderer: rewardCellRenderer,
      },
      {
        field: 'total',
        type: 'rightAligned',
        valueFormatter: rewardValueFormatter,
      },
    ];
    return colDefs;
  }, []);

  return (
    <div>
      <div className="mb-2 flex items-center justify-between gap-2">
        <h4 className="text-muted flex items-center gap-2 text-sm">
          <label htmlFor="fromEpoch">{t('From epoch')}</label>
          <EpochInput
            id="fromEpoch"
            value={epochVariables.from}
            max={epochVariables.to}
            onChange={(value) =>
              onEpochChange({
                from: value,
                to: epochVariables.to,
              })
            }
            onIncrement={() =>
              onEpochChange({
                from: epochVariables.from + 1,
                to: epochVariables.to,
              })
            }
            onDecrement={() =>
              onEpochChange({
                from: epochVariables.from - 1,
                to: epochVariables.to,
              })
            }
          />

          <label htmlFor="toEpoch">{t('to')}</label>

          <EpochInput
            id="toEpoch"
            value={epochVariables.to}
            max={epoch}
            onChange={(value) =>
              onEpochChange({
                from: epochVariables.from,
                to: value,
              })
            }
            onIncrement={() =>
              onEpochChange({
                from: epochVariables.from,
                to: epochVariables.to + 1,
              })
            }
            onDecrement={() =>
              onEpochChange({
                from: epochVariables.from,
                to: epochVariables.to - 1,
              })
            }
          />
        </h4>

        <div className="flex gap-0.5">
          <TradingButton
            onClick={() => setIsParty(false)}
            size="extra-small"
            minimal={isParty}
          >
            {t('Total distributed')}
          </TradingButton>
          <TradingButton
            onClick={() => setIsParty(true)}
            size="extra-small"
            disabled={!pubKey}
            minimal={!isParty}
          >
            {t('Earned by me')}
          </TradingButton>
        </div>
      </div>
      <AgGrid
        columnDefs={columnDefs}
        defaultColDef={defaultColDef}
        rowData={rowData}
        rowHeight={45}
        domLayout="autoHeight"
        // Show loading message without wiping out the current rows
        overlayNoRowsTemplate={loading ? t('Loading...') : t('No rows')}
      />
    </div>
  );
};

const EpochInput = ({
  id,
  value,
  max,
  min = 1,
  step = 1,
  onChange,
  onIncrement,
  onDecrement,
}: {
  id: string;
  value: number;
  max?: number;
  min?: number;
  step?: number;
  onChange: (value: number) => void;
  onIncrement: () => void;
  onDecrement: () => void;
}) => {
  return (
    <span className="flex gap-0.5" data-testid={id}>
      <span className="bg-vega-clight-600 dark:bg-vega-cdark-600 relative rounded-l-sm">
        <span className="px-2 opacity-0">{value}</span>
        <input
          onChange={(e) => onChange(Number(e.target.value))}
          value={value}
          className="dark:focus:bg-vega-cdark-700 absolute left-0 top-0 h-full w-full appearance-none bg-transparent px-2 focus:outline-none"
          type="number"
          step={step}
          min={min}
          max={max}
          id={id}
          name={id}
        />
      </span>
      <span className="flex flex-col gap-0.5 overflow-hidden rounded-r-sm">
        <button
          onClick={onIncrement}
          className="bg-vega-clight-600 dark:bg-vega-cdark-600 flex flex-1 items-center px-1"
        >
          <VegaIcon name={VegaIconNames.CHEVRON_UP} size={12} />
        </button>
        <button
          onClick={onDecrement}
          className="bg-vega-clight-600 dark:bg-vega-cdark-600 flex flex-1 items-center px-1"
        >
          <VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={12} />
        </button>
      </span>
    </span>
  );
};