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

import { AlignedText } from '@actual-app/components/aligned-text';
import { theme } from '@actual-app/components/theme';
import type { SpendingEntity } from '@actual-app/core/types/models';
import { css } from '@emotion/css';
import {
  Area,
  AreaChart,
  CartesianGrid,
  Tooltip,
  XAxis,
  YAxis,
} from 'recharts';

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

import { computePadding } from './util/computePadding';

type PayloadItem = {
  value: number;
  payload: {
    totalAssets: number | string;
    totalDebts: number | string;
    totalTotals: number | string;
    day: string;
    months: Record<
      string,
      {
        date: string;
        cumulative: number;
      }
    >;
  };
};

type CustomTooltipProps = {
  active?: boolean;
  payload?: PayloadItem[];
  balanceTypeOp: 'cumulative';
  selection: string | 'budget' | 'average';
  compare: string;
  format: (value: unknown, type?: FormatType) => string;
};

const CustomTooltip = ({
  active,
  payload,
  balanceTypeOp,
  selection,
  compare,
  format,
}: CustomTooltipProps) => {
  const { t } = useTranslation();

  if (active && payload && payload.length) {
    const comparison = ['average', 'budget'].includes(selection)
      ? payload[0].payload[selection] * -1
      : (payload[0].payload.months[selection]?.cumulative ?? 0) * -1;
    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>
              <Trans>
                Day:{' '}
                {{
                  dayOfMonth:
                    Number(payload[0].payload.day) >= 28
                      ? t('28+')
                      : payload[0].payload.day,
                }}
              </Trans>
            </strong>
          </div>
          <div style={{ lineHeight: 1.5 }}>
            {payload[0].payload.months[compare]?.cumulative ? (
              <AlignedText
                left={t('Compare:')}
                right={
                  <FinancialText>
                    {format(
                      payload[0].payload.months[compare]?.cumulative * -1,
                      'financial',
                    )}
                  </FinancialText>
                }
              />
            ) : null}
            {['cumulative'].includes(balanceTypeOp) && (
              <AlignedText
                left={
                  selection === 'average'
                    ? t('Average:')
                    : selection === 'budget'
                      ? t('Budgeted:')
                      : t('To:')
                }
                right={
                  <FinancialText>
                    {format(Math.round(comparison), 'financial')}
                  </FinancialText>
                }
              />
            )}
            {payload[0].payload.months[compare]?.cumulative ? (
              <AlignedText
                left={t('Difference:')}
                right={
                  <FinancialText>
                    {format(
                      Math.round(
                        payload[0].payload.months[compare]?.cumulative * -1 -
                          comparison,
                      ),
                      'financial',
                    )}
                  </FinancialText>
                }
              />
            ) : null}
          </div>
        </div>
      </div>
    );
  }
};

type SpendingGraphProps = {
  style?: CSSProperties;
  data: SpendingEntity;
  compact?: boolean;
  mode: 'single-month' | 'budget' | 'average';
  compare: string;
  compareTo: string;
};

export function SpendingGraph({
  style,
  data,
  compact,
  mode,
  compare,
  compareTo,
}: SpendingGraphProps) {
  const privacyMode = usePrivacyMode();
  const animationProps = useRechartsAnimation({ animationDuration: 1000 });
  const balanceTypeOp = 'cumulative';
  const format = useFormat();

  const selection = mode === 'single-month' ? compareTo : mode;

  const thisMonthMax = data.intervalData.reduce((a, b) =>
    a.months[compare]?.[balanceTypeOp] < b.months[compare]?.[balanceTypeOp]
      ? a
      : b,
  ).months[compare]?.[balanceTypeOp];
  const selectionMax = ['average', 'budget'].includes(selection)
    ? data.intervalData[27][selection]
    : data.intervalData.reduce((a, b) =>
        a.months[selection]?.[balanceTypeOp] <
        b.months[selection]?.[balanceTypeOp]
          ? a
          : b,
      ).months[selection]?.[balanceTypeOp];
  const maxYAxis = selectionMax > thisMonthMax;
  const dataMax = Math.max(
    ...data.intervalData.map(i => i.months[compare]?.cumulative),
  );
  const dataMin = Math.min(
    ...data.intervalData.map(i => i.months[compare]?.cumulative),
  );

  const tickFormatter: ComponentProps<typeof YAxis>['tickFormatter'] = tick => {
    if (!privacyMode) return `${format(tick, 'financial-no-decimals')}`;
    return '...';
  };

  const gradientOffset = () => {
    if (!dataMax || dataMax <= 0) {
      return 0;
    }
    if (!dataMin || dataMin >= 0) {
      return 1;
    }

    return dataMax / (dataMax - dataMin);
  };

  const getVal = (obj, month) => {
    if (['average', 'budget'].includes(month)) {
      return obj[month] && -1 * obj[month];
    } else {
      return (
        obj.months[month]?.[balanceTypeOp] &&
        -1 * obj.months[month][balanceTypeOp]
      );
    }
  };

  const getDate = obj => {
    return Number(obj.day) >= 28 ? '28+' : obj.day;
  };

  return (
    <Container
      style={{
        ...style,
        ...(compact && { height: 'auto' }),
      }}
    >
      {(width, height) =>
        data.intervalData && (
          <div>
            {!compact && <div style={{ marginTop: '5px' }} />}
            <AreaChart
              responsive
              width={width}
              height={height}
              data={data.intervalData}
              margin={{
                top: 0,
                right: 0,
                left: computePadding(
                  data.intervalData
                    .map(item => getVal(item, maxYAxis ? compare : selection))
                    .filter(value => value !== undefined),
                  (value: number) => format(value, 'financial-no-decimals'),
                ),
                bottom: 0,
              }}
            >
              {compact ? null : (
                <CartesianGrid strokeDasharray="3 3" vertical={false} />
              )}
              {compact ? null : (
                <XAxis
                  dataKey={val => getDate(val)}
                  tick={{ fill: theme.pageText }}
                  tickLine={{ stroke: theme.pageText }}
                />
              )}
              {compact ? null : (
                <YAxis
                  dataKey={val => getVal(val, maxYAxis ? compare : selection)}
                  domain={[0, 'auto']}
                  tickFormatter={tickFormatter}
                  tick={{ fill: theme.pageText }}
                  tickLine={{ stroke: theme.pageText }}
                  tickSize={0}
                />
              )}
              <Tooltip
                content={
                  <CustomTooltip
                    balanceTypeOp={balanceTypeOp}
                    selection={selection}
                    compare={compare}
                    format={format}
                  />
                }
                formatter={numberFormatterTooltip}
                isAnimationActive={false}
              />
              <defs>
                <linearGradient
                  id={`fill${balanceTypeOp}`}
                  x1="0"
                  y1="0"
                  x2="0"
                  y2="1"
                >
                  <stop
                    offset={gradientOffset()}
                    stopColor={theme.reportsChartFill}
                    stopOpacity={0.2}
                  />
                </linearGradient>
                <linearGradient
                  id={`stroke${balanceTypeOp}`}
                  x1="0"
                  y1="0"
                  x2="0"
                  y2="1"
                >
                  <stop
                    offset={gradientOffset()}
                    stopColor={theme.reportsChartFill}
                    stopOpacity={1}
                  />
                </linearGradient>
              </defs>

              <Area
                type="linear"
                dot={false}
                activeDot={{
                  fill: theme.reportsChartFill,
                  fillOpacity: 1,
                  r: 10,
                }}
                {...animationProps}
                dataKey={val => getVal(val, compare)}
                stroke={`url(#stroke${balanceTypeOp})`}
                strokeWidth={3}
                fill={`url(#fill${balanceTypeOp})`}
                fillOpacity={1}
              />
              <Area
                type="linear"
                dot={false}
                activeDot={false}
                {...animationProps}
                dataKey={val => getVal(val, selection)}
                stroke={theme.reportsGray}
                strokeDasharray="10 10"
                strokeWidth={3}
                fill={theme.reportsGray}
                fillOpacity={0.2}
              />
            </AreaChart>
          </div>
        )
      }
    </Container>
  );
}
