import { reactive, ref, type Ref } from 'vue';
import PatternFunctions from './PatternFunctions';
import { addAlpha } from './ColorFunctions';
import type { HTMLLegendPlugin } from '../types/Chart';
import {
  getOrCreateLegendList,
  createHtmlLegendListElement,
  createLegendElementWithPatterns,
  createLegendElementWithCheckbox,
  createHtmlLegendItemText,
  createLegendElementWithSquareArea,
  type ChartItem
} from './ChartsCommonLegend';

const { getPatternIndexWithShift } = PatternFunctions();

export default function () {
  const borderWidth = ref(2);
  const onHoverIndex: { dataSetIndex: number; columnIndex: number } = reactive({
    dataSetIndex: -1,
    columnIndex: -1
  });

  interface Dataset {
    data: any;
    label: string;
    type: any;
    fill?: boolean;
  }

  function getHtmlLegendPlugin(
    legendContainer: Ref,
    selectMode: Ref<boolean>,
    onHoverIndex: any,
    disableAccessibility: Ref<boolean>,
    patternsColors: Ref<string[]>,
    patternsList: Ref<
      ((
        hover: boolean,
        color: string,
        disableAccessibility: boolean
      ) => CanvasPattern)[]
    >
  ): HTMLLegendPlugin[] {
    return [
      {
        id: 'htmlLegend',
        afterUpdate(chart: any) {
          const ul: HTMLLIElement = getOrCreateLegendList(
            legendContainer,
            'column'
          );
          ul.style.display = 'flex';
          ul.style.margin = '1.375rem 1.0625rem';
          ul.style.flexDirection = 'row-reverse';
          ul.style.flexWrap = 'wrap';
          ul.style.justifyContent = 'flex-end';
          while (ul.firstChild) {
            ul.firstChild.remove();
          }
          const items: ChartItem[] =
            chart.options.plugins.legend.labels.generateLabels(chart);
          items
            .sort((a, b) => b.datasetIndex - a.datasetIndex)
            .forEach((item: ChartItem): void => {
              const li: HTMLElement = createHtmlLegendListElement(
                chart,
                selectMode,
                item.datasetIndex
              );
              let liContent: HTMLElement;
              if (!selectMode.value) {
                if (item?.lineCap) {
                  liContent = createLegendElementWithSquareArea(item);
                } else {
                  liContent = createLegendElementWithPatterns(
                    item,
                    chart,
                    { datasetIndex: -1 },
                    disableAccessibility.value,
                    patternsColors.value,
                    patternsList.value,
                    false
                  );
                }
              } else {
                liContent = createLegendElementWithCheckbox(
                  chart,
                  item,
                  selectMode,
                  { datasetIndex: -1 },
                  patternsColors.value,
                  false
                );
              }
              liContent.style.boxSizing = 'border-box';
              li.style.marginRight = '10px';
              li.style.marginBottom = '2px';
              li.appendChild(liContent);
              li.appendChild(createHtmlLegendItemText(item));
              ul.appendChild(li);
            });
        }
      }
    ];
  }

  function privateGetHtmlLegendPlugin(
    legendContainer: Ref,
    selectMode: Ref<boolean>,
    disableAccessibility: Ref<boolean>,
    patternsColors: Ref<string[]>,
    patternsList: Ref<
      ((
        hover: boolean,
        color: string,
        disableAccessibility: boolean
      ) => CanvasPattern)[]
    >
  ) {
    return getHtmlLegendPlugin(
      legendContainer,
      selectMode,
      onHoverIndex,
      disableAccessibility,
      patternsColors,
      patternsList
    );
  }

  function getMixedDatasets(
    datasets: Dataset[],
    disableAccessibility: boolean,
    patternsColors: string[],
    patternsList: ((
      hover: boolean,
      color: string,
      disableAccessibility: boolean
    ) => CanvasPattern)[],
    patternShifting?: number
  ) {
    // Hack to force refresh
    const borderWithValue = borderWidth.value;
    return datasets.map((dataset, index) => {
      const isBarChart = dataset.type === 'bar';
      return {
        type: dataset.type,
        fill: isBarChart ? null : false,
        borderWidth: function () {
          return disableAccessibility && isBarChart ? 1 : borderWithValue;
        },
        borderColor: function (context: any) {
          return disableAccessibility && isBarChart
            ? '#00000000'
            : isBarChart
              ? getBorderColor(
                  index,
                  context.index,
                  patternsColors,
                  patternShifting
                )
              : patternsColors[index % patternsColors.length];
        },
        backgroundColor: function (context: any) {
          return getPattern(
            index,
            context.index,
            disableAccessibility,
            patternsColors,
            patternsList,
            patternShifting
          );
        },
        yAxisID: isBarChart ? 'A' : 'B',
        pointStyle: index % 2 === 0 ? 'rectRot' : 'circle',
        data: dataset.data,
        label: dataset.label,
        pointBackgroundColor: '#FFFFFF',
        pointRadius: 5,
        order: dataset.type === 'line' ? 0 : 1
      };
    });
  }

  function getBorderColor(
    dataSetIndex: number,
    contextIndex: number,
    patternsColors: string[],
    patternShifting?: number
  ) {
    const index = getPatternIndexWithShift(dataSetIndex, patternShifting);
    if (displayFullOpacity(dataSetIndex, contextIndex)) {
      return patternsColors[index];
    } else {
      return addAlpha(patternsColors[index], 0.2);
    }
  }

  function getPattern(
    dataSetIndex: number,
    contextIndex: number,
    disableAccessibility: boolean,
    patternsColors: string[],
    patternsList: ((
      hover: boolean,
      color: string,
      disableAccessibility: boolean
    ) => CanvasPattern)[],
    patternShifting?: number
  ) {
    const index = getPatternIndexWithShift(dataSetIndex, patternShifting);
    if (displayFullOpacity(dataSetIndex, contextIndex)) {
      return patternsList[index](
        false,
        patternsColors[index],
        disableAccessibility
      );
    } else {
      return patternsList[index](
        true,
        patternsColors[index],
        disableAccessibility
      );
    }
  }

  function displayFullOpacity(
    dataSetIndex: number,
    contextIndex: number
  ): boolean {
    return (
      nothingHovered() ||
      columnHovered(dataSetIndex, contextIndex) ||
      legendHovered(dataSetIndex)
    );
  }

  function nothingHovered(): boolean {
    return onHoverIndex.dataSetIndex < 0;
  }

  function columnHovered(dataSetIndex: number, contextIndex: number): boolean {
    return (
      onHoverIndex.dataSetIndex === dataSetIndex &&
      onHoverIndex.columnIndex === contextIndex
    );
  }

  function legendHovered(dataSetIndex: number): boolean {
    return (
      onHoverIndex.dataSetIndex === dataSetIndex && onHoverIndex.columnIndex < 0
    );
  }

  return {
    privateGetHtmlLegendPlugin,
    getMixedDatasets,
    getBorderColor,
    getPattern,
    onHoverIndex
  };
}
