// @ts-strict-ignore
import React, { useMemo, useState } from 'react';
import type { CSSProperties } from 'react';
import { useTranslation } from 'react-i18next';

import { AlignedText } from '@actual-app/components/aligned-text';
import { theme } from '@actual-app/components/theme';
import type {
  balanceTypeOpType,
  DataEntity,
  LegendEntity,
  RuleConditionEntity,
} from '@actual-app/core/types/models';
import { css } from '@emotion/css';
import {
  Bar,
  BarChart,
  CartesianGrid,
  LabelList,
  Tooltip,
  XAxis,
  YAxis,
} from 'recharts';

import { FinancialText } from '#components/FinancialText';
import { useRechartsAnimation } from '#components/reports/chart-theme';
import { Container } from '#components/reports/Container';
import { getCustomTick } from '#components/reports/getCustomTick';
import { numberFormatterTooltip } from '#components/reports/numberFormatter';
import { useAccounts } from '#hooks/useAccounts';
import { useCategories } from '#hooks/useCategories';
import { useFormat } from '#hooks/useFormat';
import type { FormatType } from '#hooks/useFormat';
import { useNavigate } from '#hooks/useNavigate';
import { usePrivacyMode } from '#hooks/usePrivacyMode';

import { renderCustomLabel } from './renderCustomLabel';
import { showActivity } from './showActivity';

type PayloadItem = {
  name: string;
  value: number;
  color: string;
  payload: {
    name: string;
    color: number | string;
  };
};

type CustomTooltipProps = {
  compact: boolean;
  tooltip: string;
  legend: LegendEntity[];
  active?: boolean;
  payload?: PayloadItem[];
  label?: string;
  format: (value: unknown, type: FormatType) => string;
};

const CustomTooltip = ({
  compact,
  tooltip,
  legend,
  active,
  payload,
  label,
  format,
}: CustomTooltipProps) => {
  const { t } = useTranslation();

  const dataKeyToName = useMemo(() => {
    return new Map(legend.map(entry => [entry.dataKey, entry.name]));
  }, [legend]);

  const { sumTotals, items } = useMemo(() => {
    return (payload ?? [])
      .slice(0)
      .reverse()
      .reduce(
        (acc, item) => {
          acc.sumTotals += item.value;
          acc.items.push(item);
          return acc;
        },
        {
          sumTotals: 0,
          items: [] as PayloadItem[],
        },
      );
  }, [payload]);

  const maxTooltipItems = 5;

  const visibleItems = useMemo(() => {
    const nonZero = items.filter(p => p.value !== 0);
    if (!compact || nonZero.length <= maxTooltipItems) return nonZero;

    const hoveredIndex = nonZero.findIndex(p => tooltip === p.name);
    if (hoveredIndex >= maxTooltipItems) {
      return [...nonZero.slice(0, maxTooltipItems - 1), nonZero[hoveredIndex]];
    }
    return nonZero.slice(0, maxTooltipItems);
  }, [compact, items, tooltip]);

  if (active && items.length) {
    return (
      <div
        className={css({
          zIndex: 1000,
          pointerEvents: 'none',
          borderRadius: 2,
          boxShadow: '0 1px 6px rgba(0, 0, 0, .20)',
          backgroundColor: theme.menuBackground,
          color: theme.menuItemText,
          padding: 10,
        })}
      >
        <div>
          <div style={{ marginBottom: 10 }}>
            <strong>{label}</strong>
          </div>
          <div style={{ lineHeight: 1.4 }}>
            {visibleItems.map(pay => {
              const displayName = dataKeyToName.get(pay.name) ?? pay.name;
              return (
                <AlignedText
                  key={pay.name}
                  left={displayName}
                  right={
                    <FinancialText>
                      {format(pay.value, 'financial')}
                    </FinancialText>
                  }
                  style={{
                    color: pay.color,
                    textDecoration:
                      tooltip === pay.name ? 'underline' : 'inherit',
                  }}
                />
              );
            })}
            {compact && items.length > visibleItems.length && '...'}
            <AlignedText
              left={t('Total')}
              right={
                <FinancialText>{format(sumTotals, 'financial')}</FinancialText>
              }
              style={{
                fontWeight: 600,
              }}
            />
          </div>
        </div>
      </div>
    );
  }
};

const customLabel = props => {
  const calcX = props.x + props.width / 2;
  const calcY = props.y + props.height / 2;
  const textAnchor = 'middle';
  const display =
    props.value && `${props.format(props.value, 'financial-no-decimals')}`;
  const textSize = '12px';
  const showLabel = props.height;
  const showLabelThreshold = 20;
  const fill = theme.reportsInnerLabel;

  return renderCustomLabel(
    calcX,
    calcY,
    textAnchor,
    display,
    textSize,
    showLabel,
    showLabelThreshold,
    fill,
  );
};

type StackedBarGraphProps = {
  style?: CSSProperties;
  data: DataEntity;
  filters: RuleConditionEntity[];
  groupBy: string;
  compact?: boolean;
  viewLabels: boolean;
  balanceTypeOp: balanceTypeOpType;
  showHiddenCategories?: boolean;
  showOffBudget?: boolean;
  showTooltip?: boolean;
  interval?: string;
};

export function StackedBarGraph({
  style,
  data,
  filters,
  groupBy,
  compact,
  viewLabels,
  balanceTypeOp,
  showHiddenCategories,
  showOffBudget,
  showTooltip = true,
  interval,
}: StackedBarGraphProps) {
  const animationProps = useRechartsAnimation();
  const navigate = useNavigate();
  const { data: categories = { grouped: [], list: [] } } = useCategories();
  const { data: accounts = [] } = useAccounts();
  const privacyMode = usePrivacyMode();
  const format = useFormat();

  const customLabelWithFormat = props => customLabel({ ...props, format });

  const [pointer, setPointer] = useState('');
  const [tooltip, setTooltip] = useState('');

  const largestValue = data.intervalData
    .map(c => c[balanceTypeOp])
    .reduce((acc, cur) => (Math.abs(cur) > Math.abs(acc) ? cur : acc), 0);

  const leftMargin = Math.abs(largestValue) > 1000000 ? 20 : 0;

  return (
    <Container
      style={{
        ...style,
        ...(compact && { height: 'auto' }),
      }}
    >
      {(width, height) =>
        data.intervalData && (
          <div>
            {!compact && <div style={{ marginTop: '15px' }} />}
            <BarChart
              width={width}
              height={height}
              data={data.intervalData}
              margin={{ top: 0, right: 0, left: leftMargin, bottom: 10 }}
              style={{ cursor: pointer }}
              stackOffset="sign" //stacked by sign
            >
              {showTooltip && (
                <Tooltip
                  content={
                    <CustomTooltip
                      compact={compact}
                      tooltip={tooltip}
                      legend={data.legend}
                      format={format}
                    />
                  }
                  formatter={numberFormatterTooltip}
                  isAnimationActive={false}
                  cursor={{ fill: 'transparent' }}
                />
              )}
              <XAxis
                dataKey="date"
                tick={{ fill: theme.pageText }}
                tickLine={{ stroke: theme.pageText }}
              />
              {!compact && <CartesianGrid strokeDasharray="3 3" />}
              {!compact && (
                <YAxis
                  tickFormatter={value =>
                    getCustomTick(
                      format(value, 'financial-no-decimals'),
                      privacyMode,
                    )
                  }
                  tick={{ fill: theme.pageText }}
                  tickLine={{ stroke: theme.pageText }}
                  tickSize={0}
                />
              )}
              {data.legend
                .slice(0)
                .reverse()
                .map(entry => (
                  <Bar
                    key={entry.dataKey}
                    dataKey={entry.dataKey}
                    stackId="a"
                    fill={entry.color}
                    {...animationProps}
                    onMouseLeave={() => {
                      setPointer('');
                      setTooltip('');
                    }}
                    onMouseEnter={() => {
                      setTooltip(entry.dataKey);
                      if (groupBy !== 'Interval') {
                        setPointer('pointer');
                      }
                    }}
                    onClick={e =>
                      ((compact && showTooltip) || !compact) &&
                      groupBy !== 'Interval' &&
                      showActivity({
                        navigate,
                        categories,
                        accounts,
                        balanceTypeOp,
                        filters,
                        showHiddenCategories,
                        showOffBudget,
                        type: 'time',
                        startDate: e.payload?.intervalStartDate,
                        endDate: e.payload?.intervalEndDate,
                        field: groupBy.toLowerCase(),
                        id: entry.id,
                        uncategorizedId: entry.uncategorizedId,
                        interval,
                      })
                    }
                  >
                    {viewLabels && !compact && (
                      <LabelList
                        dataKey={entry.dataKey}
                        content={customLabelWithFormat}
                      />
                    )}
                  </Bar>
                ))}
            </BarChart>
          </div>
        )
      }
    </Container>
  );
}
