import React, { memo, useCallback, useMemo } from 'react';
import {
  Area,
  Bar,
  CartesianGrid,
  ComposedChart,
  Label,
  Line,
  ReferenceLine,
  ResponsiveContainer,
  Tooltip,
  XAxis,
  YAxis
} from 'recharts';
import type { Margin } from 'recharts/types/util/types';
import { useTheme } from '@mui/material/styles';
import {
  useEmpty,
  useTick,
  ChartTooltip,
  formatterData,
  setDomainCalc,
  formatterId,
  fixChartsClasses
} from '../../utils';
import type { ChartTooltipProps } from '../../utils';
import type { Theme } from '../@styles/theme-provider';

export interface ChartComposedProps {
  data?: Record<string, string | number | undefined>[];
  tooltip?: boolean;
  tooltipPostfix?: string;
  height?: number;
  dots?: boolean;
  domainMax?: number;
  domainCalc?: boolean;
  tickCountY?: number;
  YAxisUnit?: string;
  dataKeyX?: ChartTooltipProps['format'];
  dataXFormat?: ChartTooltipProps['dataXFormat'];
  dataYFormat?: ChartTooltipProps['dataYFormat'];
  tooltipFormatter?: ChartTooltipProps['tooltipFormatter'];
  chartConfig?: ChartTooltipProps['config'] &
    {
      type: string;
      yAxisSecond?: boolean;
    }[];
  isSecondYAxis?: boolean;
  secondYAxisHide: boolean;
  secondYAxisDomainMax: number;
  secondYAxisDomainCalc: boolean;
  secondYAxisDomainCalcMultiplier?: number;
  referenceValue?: number;
  referenceTitle?: string;
  referenceWidth?: number;
  chartMargin?: Margin;
  tooltipProps?: Partial<ChartTooltipProps>;
}

const ChartComposed = (props: ChartComposedProps) => {
  const theme = useTheme<Theme>();
  const empty = useEmpty();
  const {
    data = [],
    chartConfig = [empty],
    domainMax = 100,
    dots,
    height,
    tooltip,
    tooltipPostfix,
    domainCalc = false,
    tickCountY,
    dataKeyX = 'date',
    dataXFormat = 'month',
    dataYFormat = '',
    tooltipFormatter,
    chartMargin = { left: -24, right: 6 },
    isSecondYAxis = false,
    secondYAxisHide = false,
    secondYAxisDomainMax,
    secondYAxisDomainCalc,
    secondYAxisDomainCalcMultiplier = 1,
    referenceValue,
    referenceTitle,
    referenceWidth,
    YAxisUnit,
    tooltipProps
  } = props;

  const classes = fixChartsClasses();

  const tick = useTick();
  const newConfig = useMemo(() => [...chartConfig].reverse(), [chartConfig]);
  const formatterX = useCallback(
    (value: string) => formatterData(value, dataKeyX, dataXFormat),
    [dataKeyX, dataXFormat]
  );
  const formatterY = useCallback(
    (value: string) => formatterData(value, '', dataYFormat),
    [dataYFormat]
  );
  const setDomain = useCallback(
    (dataMax: number | undefined) => setDomainCalc(dataMax, domainCalc, domainMax),
    [domainMax, domainCalc]
  );

  const setDomainSecondYAxis = useCallback(
    (dataMax: number | undefined) =>
      setDomainCalc(
        dataMax,
        secondYAxisDomainCalc,
        secondYAxisDomainMax,
        secondYAxisDomainCalcMultiplier
      ),
    [secondYAxisDomainMax, secondYAxisDomainCalc, secondYAxisDomainCalcMultiplier]
  );

  const ReferenceLabel = useCallback(
    ({ viewBox }) => {
      const xRect = viewBox.x;
      const yRect = viewBox.y - 9;
      const xText = xRect + 4;
      const yText = yRect + 13;

      return (
        <g x={0} y={0}>
          <rect
            fill={theme.palette.primary[600]}
            width={referenceWidth}
            height={18}
            x={xRect}
            y={yRect}
          />
          <text
            x={xText}
            y={yText}
            fill={theme.palette.common.white}
            fontSize={12}
            fontWeight={500}
            fontFamily={theme.typography.fontFamily}
          >
            {referenceTitle}
          </text>
        </g>
      );
    },
    [referenceTitle, referenceWidth, theme]
  );

  return (
    <ResponsiveContainer width="100%" height={height} className={classes.root}>
      <ComposedChart data={data} margin={chartMargin}>
        <defs>
          {newConfig?.map((entry, index) => (
            <linearGradient key={entry.id} id={formatterId(entry.id)} x1="0" y1="0" x2="0" y2="1">
              <stop
                offset="5%"
                stopColor={entry.color || theme.palette.specialColors[newConfig.length - index]}
                stopOpacity={0.4}
              />
              <stop offset="95%" stopColor={theme.palette.common.white} stopOpacity={0} />
            </linearGradient>
          ))}
        </defs>
        <CartesianGrid vertical={false} stroke={theme.palette.primary[300]} strokeDasharray="2 2" />
        <XAxis
          dataKey={dataKeyX}
          tick={tick}
          axisLine={false}
          tickLine={false}
          tickCount={data.length}
          minTickGap={1}
          tickFormatter={formatterX}
          tickMargin={8}
        />
        <YAxis
          padding={{ top: 8 }}
          tickCount={tickCountY}
          scale="linear"
          domain={[0, setDomain]}
          tick={tick}
          axisLine={false}
          tickLine={false}
          minTickGap={1}
          tickFormatter={formatterY}
          yAxisId="left"
          tickMargin={2}
          unit={YAxisUnit}
          width={YAxisUnit ? 100 : 60}
        />
        {isSecondYAxis && (
          <YAxis
            tickCount={tickCountY}
            scale="linear"
            domain={[0, setDomainSecondYAxis]}
            tick={tick}
            axisLine={false}
            tickLine={false}
            minTickGap={1}
            tickFormatter={formatterY}
            yAxisId="right"
            orientation="right"
            hide={secondYAxisHide}
            tickMargin={2}
            padding={{ top: 8 }}
          />
        )}
        {tooltip && (
          <Tooltip
            content={
              <ChartTooltip
                format={dataKeyX}
                dataXFormat={dataXFormat}
                dataYFormat={dataYFormat}
                tooltipFormatter={tooltipFormatter}
                config={newConfig}
                valuePostfix={tooltipPostfix}
              />
            }
            {...tooltipProps}
          />
        )}
        {newConfig?.map((entry, index) => {
          if (entry.show) {
            if (entry.type === 'area') {
              return (
                <Area
                  key={`cell-${entry.id}`}
                  type="monotone"
                  dataKey={entry.id}
                  stroke={entry.color || theme.palette.specialColors[newConfig.length - index]}
                  fill={`url(#${formatterId(entry.id)})`}
                  strokeWidth={2}
                  fillOpacity={1}
                  dot={
                    dots
                      ? {
                          stroke:
                            entry.color || theme.palette.specialColors[newConfig.length - index],
                          strokeWidth: 2,
                          r: 3,
                          fill: theme.palette.common.white
                        }
                      : false
                  }
                  activeDot={{
                    stroke: entry.color || theme.palette.specialColors[newConfig.length - index],
                    strokeWidth: 2,
                    r: 5,
                    fill: theme.palette.common.white
                  }}
                  connectNulls
                  yAxisId={entry.yAxisSecond ? 'right' : 'left'}
                />
              );
            }
            if (entry.type === 'line') {
              return (
                <Line
                  key={`cell-${entry.id}`}
                  type="monotone"
                  dataKey={entry.id}
                  stroke={entry.color || theme.palette.specialColors[newConfig.length - index]}
                  strokeWidth={2}
                  fillOpacity={1}
                  dot={
                    dots
                      ? {
                          stroke:
                            entry.color || theme.palette.specialColors[newConfig.length - index],
                          strokeWidth: 2,
                          r: 3,
                          fill: theme.palette.common.white
                        }
                      : false
                  }
                  activeDot={{
                    stroke: entry.color || theme.palette.specialColors[newConfig.length - index],
                    strokeWidth: 2,
                    r: 5,
                    fill: theme.palette.common.white
                  }}
                  connectNulls
                  yAxisId={entry.yAxisSecond ? 'right' : 'left'}
                />
              );
            }
            if (entry.type === 'bar') {
              return (
                <Bar
                  key={`cell-${entry.id}`}
                  type="monotone"
                  dataKey={entry.id}
                  fill={entry.color || theme.palette.specialColors[newConfig.length - index]}
                  strokeWidth={2}
                  fillOpacity={1}
                  barSize={40}
                  stackId="a"
                  radius={[
                    newConfig.length === index + 1 ? 4 : 0,
                    newConfig.length === index + 1 ? 4 : 0,
                    0,
                    0
                  ]}
                  yAxisId={entry.yAxisSecond && isSecondYAxis ? 'right' : 'left'}
                />
              );
            }
            return null;
          }
          return null;
        })}
        {referenceValue && (
          <ReferenceLine
            stroke={theme.palette.primary[600]}
            strokeWidth={2}
            y={referenceValue}
            yAxisId="left"
          >
            <Label
              position="insideLeft"
              fill={theme.palette.common.white}
              content={ReferenceLabel}
            />
          </ReferenceLine>
        )}
      </ComposedChart>
    </ResponsiveContainer>
  );
};

ChartComposed.defaultProps = {
  data: [],
  chartConfig: [],
  tooltip: true,
  height: 325,
  dots: false,
  domainCalc: false,
  domainMax: 100,
  tickCountY: 10,
  isSecondYAxis: false,
  secondYAxisHide: false,
  secondYAxisDomainMax: 100,
  secondYAxisDomainCalc: false,
  secondYAxisDomainCalcMultiplier: 1,
  chartMargin: { left: -24, right: 6 },
  dataKeyX: 'date',
  dataXFormat: 'month',
  dataYFormat: '',
  referenceValue: 5
};

const m = memo(ChartComposed);
export { m as ChartComposed };
