import type { Ref } from 'vue';
import type { HTMLLegendPlugin } from '../types/Chart';
import type { ChartOptions } from 'chart.js';
import PatternFunctions from './PatternFunctions';
import { formatValueAndRate } from './FormatUtilities';
import QuestionMarkSvg from '@mozaic-ds/icons/svg/Navigation_Notification_Question_24px.svg';

const { getPatternCanvas } = PatternFunctions();

export const LEGEND_FONT_SIZE = 14;
export const LEGEND_LABEL_LEFT_MARGIN = '6px';
export const LEGEND_BOX_SIZE = '22px';
export const LEGEND_BOX_POINT_SIZE = 6;
export const LEGEND_BOX_BORDER = '2px';

export interface Chart {
  update(): void;

  toggleDataVisibility(datasetIndex: number): void;

  isDatasetVisible(datasetIndex: number): boolean;

  getDataVisibility(index: number): boolean;

  setDatasetVisibility(datasetIndex: number, visible: boolean): void;

  plugins: HTMLLegendPlugin;

  options:
    | ChartOptions<'radar'>
    | ChartOptions<'doughnut'>
    | ChartOptions<'bar'>
    | ChartOptions<'line'>;

  config: {
    type?: string;
    data: { labels: string[]; datasets: unknown[]; data?: unknown[] };
  };
}

export interface ChartItem {
  fontColor: string;
  hidden: boolean;
  text: string;
  fillStyle: string;
  strokeStyle: string;
  lineWidth: number;
  datasetIndex: number;
  index: number;
  lineCap?: string;
}

export function getHtmlLegendPlugin(
  legendContainer: Ref,
  selectMode: Ref<boolean>,
  onHoverIndex: any,
  disableAccessibility: Ref<boolean>,
  patternsColors: Ref<string[]>,
  patternsList: Ref<
    ((
      hover: boolean,
      color: string,
      disableAccessibility: boolean
    ) => CanvasPattern)[]
  >,
  enableHoverFeature: Ref<boolean>,
  maxValueToDisplay?: number,
  chartData?: any
): HTMLLegendPlugin {
  return {
    id: 'htmlLegend',
    afterUpdate(chart: any) {
      const isDoughnut: boolean = chart.config.type === 'doughnut';
      const flexDirection = isDoughnut ? 'column' : 'row';
      const ul: HTMLLIElement = getOrCreateLegendList(
        legendContainer,
        flexDirection
      );
      ul.style.margin = '1.375rem 1.0625rem';
      while (ul.firstChild) {
        ul.firstChild.remove();
      }
      const items: ChartItem[] =
        chart.options.plugins.legend.labels.generateLabels(chart);
      items.forEach((item: ChartItem): void => {
        const isDoughnut: boolean = chart.config.type === 'doughnut';
        const index: number = isDoughnut ? item.index : item.datasetIndex;
        const li: HTMLElement = createHtmlLegendListElement(
          chart,
          selectMode,
          index
        );
        if (isDoughnut) {
          const isOthersElement: boolean =
            index + 1 === maxValueToDisplay ? true : false;
          li.style.marginTop = '12px';
          if (isOthersElement) {
            li.style.position = 'relative';
          }
        } else {
          li.style.marginRight = '10px';
        }
        li.style.width = 'max-content';
        li.style.cursor = 'pointer';
        let liContent: HTMLElement;
        if (!selectMode.value) {
          liContent = createLegendElementWithPatterns(
            item,
            chart,
            onHoverIndex,
            disableAccessibility.value,
            patternsColors.value,
            patternsList.value,
            enableHoverFeature.value
          );
        } else {
          liContent = createLegendElementWithCheckbox(
            chart,
            item,
            selectMode,
            onHoverIndex,
            patternsColors.value,
            enableHoverFeature.value
          );
        }
        liContent.style.boxSizing = 'border-box';
        li.appendChild(liContent);
        li.appendChild(createHtmlLegendItemText(item));
        if (
          isDoughnut &&
          maxValueToDisplay &&
          hasOthersTooltipToDisplay(chartData, maxValueToDisplay, index)
        ) {
          li.appendChild(createTooltipAndItsIcon(chartData, maxValueToDisplay));
        }
        ul.appendChild(li);
      });
    }
  };
}

export function hasOthersTooltipToDisplay(
  doughnutData: any,
  maxValueToDisplay: number,
  index: number
) {
  return (
    doughnutData.data.length > maxValueToDisplay &&
    index === maxValueToDisplay - 1
  );
}

export function createTooltipAndItsIcon(
  doughnutData: any,
  maxValueToDisplay: number
): HTMLDivElement {
  const iconTopWrapper = document.createElement('div');
  const iconWrapper = document.createElement('div');
  const icon = document.createElement('img');
  iconTopWrapper.style.position = 'absolute';
  iconTopWrapper.style.right = '-32px';
  icon.src = QuestionMarkSvg;
  icon.style.top = '0';
  icon.style.width = '1.5rem';
  icon.style.filter =
    'invert(38%) sepia(19%) saturate(18%) hue-rotate(337deg) brightness(97%) contrast(85%)';
  iconWrapper.style.position = 'relative';
  iconWrapper.style.display = 'flex';
  const tooltip = createLegendOthersTooltip(doughnutData, maxValueToDisplay);
  icon.onmouseover = () => {
    (iconWrapper.firstElementChild as HTMLElement).style.visibility = 'visible';
  };
  icon.onmouseleave = () => {
    (iconWrapper.firstElementChild as HTMLElement).style.visibility = 'hidden';
  };
  iconTopWrapper.appendChild(iconWrapper);
  iconWrapper.appendChild(tooltip);
  iconWrapper.appendChild(icon);
  return iconTopWrapper;
}

function createLegendOthersTooltip(
  doughnutData: any,
  maxValueToDisplay: number
) {
  const tooltip = document.createElement('div');
  tooltip.style.visibility = 'hidden';
  tooltip.style.position = 'absolute';
  tooltip.style.zIndex = '10';
  tooltip.style.width = '350px';
  tooltip.style.bottom = '100%';
  tooltip.style.left = '50%';
  tooltip.style.marginLeft = '-150px';
  tooltip.style.background = '#FFFFFF';
  tooltip.style.boxShadow = '0px 1px 5px rgba(0, 0, 0, 0.2)';
  tooltip.style.borderRadius = '0.5rem';
  tooltip.style.fontSize = '14px';
  tooltip.style.overflow = 'hidden';
  addOthersTooltipLines(doughnutData, maxValueToDisplay, tooltip);
  return tooltip;
}

function addOthersTooltipLines(
  doughnutData: any,
  maxValueToDisplay: number,
  tooltip: HTMLDivElement
) {
  const startIndex = maxValueToDisplay - 1;
  doughnutData.data.slice(startIndex).forEach((_ignore: any, index: number) => {
    const dataIndex = startIndex + index;
    const textWrapper = document.createElement('div');
    textWrapper.style.display = 'flex';
    textWrapper.style.justifyContent = 'space-between';
    textWrapper.style.padding = '0.5rem';
    textWrapper.style.border = '1px solid #CCCCCC';
    const label = document.createElement('span');
    label.appendChild(document.createTextNode(doughnutData.labels[dataIndex]));
    const value = document.createElement('span');
    value.appendChild(
      document.createTextNode(formatValueAndRate(doughnutData, dataIndex))
    );
    textWrapper.appendChild(label);
    textWrapper.appendChild(value);
    tooltip.appendChild(textWrapper);
  });
}

export function createLegendElementWithPatterns(
  item: ChartItem,
  chart: Chart,
  onHoverIndex: any | null,
  disableAccessibility: boolean,
  patternsColors: string[],
  patternsList: ((
    hover: boolean,
    color: string,
    disableAccessibility: boolean
  ) => CanvasPattern)[],
  enableHoverFeature: boolean
): HTMLElement {
  const isDoughnut: boolean = chart.config.type === 'doughnut';
  const index: number = isDoughnut ? item.index : item.datasetIndex;
  const img: HTMLImageElement = new Image();
  const boxSpan: HTMLElement = createHtmlLegendLine(item, chart.config.type);
  const pattern: CanvasPattern = patternsList[index](
    false,
    patternsColors[index],
    disableAccessibility
  );
  const patternCanvas: HTMLCanvasElement = getPatternCanvas(pattern);
  img.src = patternCanvas.toDataURL();
  boxSpan.style.background = `url(${img.src})`;
  boxSpan.style.backgroundSize = 'cover';
  boxSpan.style.borderColor = patternsColors[index];
  boxSpan.style.borderWidth = LEGEND_BOX_BORDER;

  if (enableHoverFeature) {
    boxSpan.onmouseover = (): void => {
      isDoughnut
        ? (onHoverIndex.value = index)
        : (onHoverIndex.dataSetIndex = index);
    };
    boxSpan.onmouseleave = (): void => {
      isDoughnut
        ? (onHoverIndex.value = null)
        : (onHoverIndex.dataSetIndex = -1);
    };
  }
  return boxSpan;
}

export function createLegendElementWithCheckbox(
  chart: Chart,
  item: ChartItem,
  selectMode: Ref<boolean>,
  onHoverIndex: any | null,
  patternsColors: string[],
  enableHoverFeature: boolean
): HTMLElement {
  const isDoughnut: boolean = chart.config.type === 'doughnut';
  const index: number = isDoughnut ? item.index : item.datasetIndex;
  const checkbox: HTMLElement = createLegendCheckbox(
    chart,
    item,
    patternsColors
  );
  const labels = chart.config.data.labels;
  const allCheckBoxesVisible: boolean = labels.every((_, index: number) =>
    chart.getDataVisibility(index)
  );
  if (allCheckBoxesVisible) {
    if (isDoughnut) {
      selectMode.value = false;
      onHoverIndex.value = -1;
    }
    return checkbox;
  }
  if (enableHoverFeature) {
    checkbox.onmouseover = (): void => {
      isDoughnut
        ? (onHoverIndex.value = index)
        : (onHoverIndex.dataSetIndex = index);
      chart.update();
    };
    checkbox.onmouseleave = (): void => {
      isDoughnut
        ? (onHoverIndex.value = null)
        : (onHoverIndex.dataSetIndex = -1);
      chart.update();
    };
  }
  return checkbox;
}

export function createHtmlLegendItemText(item: ChartItem) {
  const textContainer = document.createElement('p');
  textContainer.style.color = item.fontColor;
  textContainer.style.fontSize = `${LEGEND_FONT_SIZE}px`;
  textContainer.style.margin = '0';
  textContainer.style.padding = '0';

  const text = document.createTextNode(item.text);
  textContainer.appendChild(text);
  return textContainer;
}

export function createHtmlLegendLine(
  item: ChartItem,
  type: string | undefined
) {
  const boxSpan = document.createElement('div');
  if (type !== 'doughnut') {
    boxSpan.style.background = 'rgba(0, 0, 0, 0.1)';
    boxSpan.style.borderColor = item.strokeStyle;
    boxSpan.style.borderWidth = LEGEND_BOX_BORDER;
  }
  boxSpan.style.borderRadius = '5px';
  boxSpan.style.borderStyle = 'solid';
  boxSpan.style.display = 'flex';
  boxSpan.style.justifyContent = 'center';
  boxSpan.style.alignItems = 'center';
  boxSpan.style.minWidth = LEGEND_BOX_SIZE;
  boxSpan.style.marginRight = LEGEND_LABEL_LEFT_MARGIN;
  boxSpan.style.minHeight = LEGEND_BOX_SIZE;
  return boxSpan;
}

export function createHtmlLegendDatasetSquare(item: ChartItem) {
  const divPoint = document.createElement('div');
  divPoint.style.height = LEGEND_BOX_POINT_SIZE + 'px';
  divPoint.style.width = LEGEND_BOX_POINT_SIZE + 'px';
  divPoint.style.background = 'white';
  divPoint.style.borderStyle = 'solid';
  divPoint.style.borderColor = item.strokeStyle;
  divPoint.style.borderWidth = LEGEND_BOX_BORDER;
  return divPoint;
}

export function createHtmlLegendListElement(
  chart: Chart,
  selectMode: Ref,
  elementIndex: number
) {
  const li: HTMLElement = document.createElement('li');
  li.style.alignItems = 'center';
  li.style.cursor = selectMode.value ? '' : 'pointer';
  li.style.display = 'flex';
  li.style.flexDirection = 'row';
  li.setAttribute('data-test-id', `legend-item-${elementIndex}`);
  li.onclick = () => {
    if (!selectMode.value) {
      hideAllButThis(chart, elementIndex, selectMode);
      chart.update();
    } else {
      switchItemVisibility(chart, elementIndex, selectMode);
    }
  };
  return li;
}

export function addCheckboxStyle(
  isDataSetVisible: boolean,
  item: ChartItem,
  checkbox: Element,
  patternColor: string
) {
  let backgroundColor = '#fff';
  let borderColor = '#666';
  if (isDataSetVisible) {
    //Default white for patterns chart
    backgroundColor = isDefaultWhiteColor(item.strokeStyle)
      ? patternColor
      : item.strokeStyle;
    borderColor = isDefaultWhiteColor(item.strokeStyle)
      ? patternColor
      : item.strokeStyle;
    checkbox.setAttribute('checked', '' + isDataSetVisible);
  }
  checkbox.setAttribute('class', 'mc-checkbox__input');
  checkbox.setAttribute(
    'style',
    `background-color: ${backgroundColor};
    min-width: ${LEGEND_BOX_SIZE};
    min-height: ${LEGEND_BOX_SIZE};
    margin-right: ${LEGEND_LABEL_LEFT_MARGIN};
    border-color: ${borderColor};`
  );
}

export function createLegendCheckbox(
  chart: Chart,
  item: ChartItem,
  patternsColors: string[]
) {
  const isDoughnut: boolean = chart.config.type === 'doughnut';
  const index: number = isDoughnut ? item.index : item.datasetIndex;
  const checkbox = document.createElement('input');
  checkbox.setAttribute('type', 'checkbox');
  checkbox.setAttribute('data-test-id', `legend-checkbox-${index}`);
  const isDataSetVisible = isChartDataVisible(chart, index);
  const patternColor = patternsColors ? patternsColors[index] : undefined;
  addCheckboxStyle(isDataSetVisible, item, checkbox, patternColor as string);
  return checkbox;
}

function isMonoDataSetChart(chart: Chart) {
  const { type } = chart.config;
  return type === 'pie' || type === 'doughnut';
}

function getChartsData(chart: any) {
  let dataSets: unknown[] = chart.config.data.datasets;
  if (isMonoDataSetChart(chart)) {
    dataSets = chart.config.data.datasets[0].data;
  }
  return dataSets;
}

export function hideAllButThis(
  chart: Chart,
  elementIndex: number,
  selectMode: Ref
) {
  if (!selectMode.value) {
    const dataSets: unknown[] = getChartsData(chart);
    selectMode.value = true;
    dataSets.forEach((_data, index) => {
      if (index !== elementIndex) {
        switchItemVisibility(chart, index);
      }
    });
  }
}

function allDataVisible(chart: Chart): boolean {
  let allVisible = true;
  const chartsData: unknown[] = getChartsData(chart);
  chartsData.forEach((_data, dataIndex) => {
    allVisible = allVisible && isChartDataVisible(chart, dataIndex);
  });
  return allVisible;
}

function isChartDataVisible(chart: Chart, dataIndex: number): boolean {
  if (isMonoDataSetChart(chart)) {
    return chart.getDataVisibility(dataIndex);
  } else {
    return chart.isDatasetVisible(dataIndex);
  }
}

export function switchItemVisibility(
  chart: Chart,
  elementIndex: number,
  selectMode?: Ref
) {
  if (isMonoDataSetChart(chart)) {
    chart.toggleDataVisibility(elementIndex);
  } else {
    chart.setDatasetVisibility(
      elementIndex,
      !chart.isDatasetVisible(elementIndex)
    );
  }

  if (selectMode && allDataVisible(chart)) {
    selectMode.value = false;
  }
  chart.update();
}

export function createLegendElementWithSquareArea(
  item: ChartItem,
  mainSerieFirstDataset?: boolean
) {
  const liContent = createHtmlLegendLine(item, '');
  const divPoint = createHtmlLegendDatasetSquare(item);
  const index = item.index || item.datasetIndex;

  divPoint.style.width = '10px';
  divPoint.style.height = '10px';
  if (index % 2 === 0) {
    mainSerieFirstDataset
      ? (divPoint.style.borderRadius = '25px')
      : (divPoint.style.transform = 'rotate(45deg)');
  } else {
    mainSerieFirstDataset
      ? (divPoint.style.transform = 'rotate(45deg)')
      : (divPoint.style.borderRadius = '25px');
  }
  liContent.appendChild(divPoint);
  return liContent;
}

export function getOrCreateLegendList(
  legendContainer: Ref,
  flexDirection: string
) {
  let listContainer = legendContainer.value?.querySelector('ul');
  if (!listContainer) {
    listContainer = document.createElement('ul');
    listContainer.style.display = 'flex';
    listContainer.style.flexDirection = flexDirection;
    listContainer.style.margin = '0';
    listContainer.style.padding = '0';
    legendContainer.value?.appendChild(listContainer);
  }
  return listContainer;
}

function isDefaultWhiteColor(color: string) {
  return color === '#00000000';
}
