import { each, get, isArray, isEmpty, isFunction, isNil, isNumber, isString, toString } from '@antv/util';
import { animation, annotation, legend, state, theme } from '../../adaptor/common';
import { interval } from '../../adaptor/geometries';
import { getMappingFunction } from '../../adaptor/geometries/base';
import { pattern } from '../../adaptor/pattern';
import { Params } from '../../core/adaptor';
import { getLocale } from '../../core/locale';
import { Data, Datum } from '../../types';
import { Interaction } from '../../types/interaction';
import { deepAssign, flow, processIllegalData, renderStatistic, template, transformLabel } from '../../utils';
import { DEFAULT_OPTIONS } from './contants';
import { PIE_STATISTIC } from './interactions';
import { PieOptions } from './types';
import { adaptOffset, getTotalValue, isAllZero } from './utils';

/**
 * 字段
 * @param params
 */
function geometry(params: Params<PieOptions>): Params<PieOptions> {
  const { chart, options } = params;
  const { data, angleField, colorField, color, pieStyle, shape } = options;

  // 处理不合法的数据
  let processData = processIllegalData(data, angleField);

  if (isAllZero(processData, angleField)) {
    // 数据全 0 处理，调整 position 映射
    const percentageField = '$$percentage$$';
    processData = processData.map((d) => ({ ...d, [percentageField]: 1 / processData.length }));
    chart.data(processData);

    const p = deepAssign({}, params, {
      options: {
        xField: '1',
        yField: percentageField,
        seriesField: colorField,
        isStack: true,
        interval: {
          color,
          shape,
          style: pieStyle,
        },
        args: {
          zIndexReversed: true,
          sortZIndex: true,
        },
      },
    });

    interval(p);
  } else {
    chart.data(processData);

    const p = deepAssign({}, params, {
      options: {
        xField: '1',
        yField: angleField,
        seriesField: colorField,
        isStack: true,
        interval: {
          color,
          shape,
          style: pieStyle,
        },
        args: {
          zIndexReversed: true,
          sortZIndex: true,
        },
      },
    });

    interval(p);
  }

  return params;
}

/**
 * meta 配置
 * @param params
 */
function meta(params: Params<PieOptions>): Params<PieOptions> {
  const { chart, options } = params;
  const { meta, colorField } = options;

  // meta 直接是 scale 的信息
  const scales = deepAssign({}, meta);
  chart.scale(scales, {
    [colorField]: { type: 'cat' },
  });

  return params;
}

/**
 * coord 配置
 * @param params
 */
function coordinate(params: Params<PieOptions>): Params<PieOptions> {
  const { chart, options } = params;
  const { radius, innerRadius, startAngle, endAngle } = options;

  chart.coordinate({
    type: 'theta',
    cfg: {
      radius,
      innerRadius,
      startAngle,
      endAngle,
    },
  });

  return params;
}

/**
 * label 配置
 * @param params
 */
function label(params: Params<PieOptions>): Params<PieOptions> {
  const { chart, options } = params;
  const { label, colorField, angleField } = options;

  const geometry = chart.geometries[0];
  // label 为 false, 空 则不显示 label
  if (!label) {
    geometry.label(false);
  } else {
    const { callback, ...cfg } = label;
    const labelCfg = transformLabel(cfg);

    // ① 提供模板字符串的 label content 配置
    if (labelCfg.content) {
      const { content } = labelCfg;
      labelCfg.content = (data: object, dataum: any, index: number) => {
        const name = data[colorField];
        const value = data[angleField];
        // dymatic get scale, scale is ready this time
        const angleScale = chart.getScaleByField(angleField);
        const percent = angleScale?.scale(value);
        return isFunction(content)
          ? // append percent (number) to data, users can get origin data from `dataum._origin`
            content({ ...data, percent }, dataum, index)
          : isString(content)
          ? template(content as string, {
              value,
              name,
              // percentage (string), default keep 2
              percentage: isNumber(percent) && !isNil(value) ? `${(percent * 100).toFixed(2)}%` : null,
            })
          : content;
      };
    }

    const LABEL_LAYOUT_TYPE_MAP = {
      inner: '',
      outer: 'pie-outer',
      spider: 'pie-spider',
    };
    const labelLayoutType = labelCfg.type ? LABEL_LAYOUT_TYPE_MAP[labelCfg.type] : 'pie-outer';
    const labelLayoutCfg = labelCfg.layout ? (!isArray(labelCfg.layout) ? [labelCfg.layout] : labelCfg.layout) : [];
    labelCfg.layout = (labelLayoutType ? [{ type: labelLayoutType }] : []).concat(labelLayoutCfg);

    geometry.label({
      // fix: could not create scale, when field is undefined（attributes 中的 fields 定义都会被用来创建 scale）
      fields: colorField ? [angleField, colorField] : [angleField],
      callback,
      cfg: {
        ...labelCfg,
        offset: adaptOffset(labelCfg.type, labelCfg.offset),
        type: 'pie',
      },
    });
  }
  return params;
}

/**
 * statistic options 处理
 * 1. 默认继承 default options 的样式
 * 2. 默认使用 meta 的 formatter
 */
export function transformStatisticOptions(options: PieOptions): PieOptions {
  const { innerRadius, statistic, angleField, colorField, meta, locale } = options;

  const i18n = getLocale(locale);

  if (innerRadius && statistic) {
    let { title: titleOpt, content: contentOpt } = deepAssign({}, DEFAULT_OPTIONS.statistic, statistic);
    if (titleOpt !== false) {
      titleOpt = deepAssign(
        {},
        {
          formatter: (datum: Datum) => {
            // 交互中, datum existed.
            const text = datum
              ? datum[colorField]
              : !isNil(titleOpt.content)
              ? titleOpt.content
              : i18n.get(['statistic', 'total']);
            const metaFormatter = get(meta, [colorField, 'formatter']) || ((v) => v);
            return metaFormatter(text);
          },
        },
        titleOpt
      );
    }
    if (contentOpt !== false) {
      contentOpt = deepAssign(
        {},
        {
          formatter: (datum: Datum, data: Data) => {
            const dataValue = datum ? datum[angleField] : getTotalValue(data, angleField);
            const metaFormatter = get(meta, [angleField, 'formatter']) || ((v) => v);
            // 交互中
            if (datum) {
              return metaFormatter(dataValue);
            }
            return !isNil(contentOpt.content) ? contentOpt.content : metaFormatter(dataValue);
          },
        },
        contentOpt
      );
    }

    return deepAssign({}, { statistic: { title: titleOpt, content: contentOpt } }, options);
  }
  return options;
}

/**
 * statistic 中心文本配置
 * @param params
 */
export function pieAnnotation(params: Params<PieOptions>): Params<PieOptions> {
  const { chart, options } = params;
  const { innerRadius, statistic } = transformStatisticOptions(options);
  // 先清空标注，再重新渲染
  chart.getController('annotation').clear(true);

  // 先进行其他 annotations，再去渲染统计文本
  flow(annotation())(params);

  /** 中心文本 指标卡 */
  if (innerRadius && statistic) {
    renderStatistic(chart, { statistic, plotType: 'pie' });
  }

  return params;
}

/**
 * 饼图 tooltip 配置
 * 1. 强制 tooltip.shared 为 false
 * @param params
 */
function tooltip(params: Params<PieOptions>): Params<PieOptions> {
  const { chart, options } = params;
  const { tooltip, colorField, angleField, data } = options;

  if (tooltip === false) {
    chart.tooltip(tooltip);
  } else {
    chart.tooltip(deepAssign({}, tooltip, { shared: false }));

    // 主要解决 all zero， 对于非 all zero 不再适用
    if (isAllZero(data, angleField)) {
      let fields = get(tooltip, 'fields');
      let formatter = get(tooltip, 'formatter');

      if (isEmpty(get(tooltip, 'fields'))) {
        fields = [colorField, angleField];
        formatter = formatter || ((datum) => ({ name: datum[colorField], value: toString(datum[angleField]) }));
      }
      chart.geometries[0].tooltip(fields.join('*'), getMappingFunction(fields, formatter));
    }
  }

  return params;
}

/**
 * Interaction 配置 (饼图特殊的 interaction, 中心文本变更的时候，需要将一些配置参数传进去）
 * @param params
 */
export function interaction(params: Params<PieOptions>): Params<PieOptions> {
  const { chart, options } = params;
  const { interactions, statistic, annotations } = transformStatisticOptions(options);

  each(interactions, (i: Interaction) => {
    if (i.enable === false) {
      chart.removeInteraction(i.type);
    } else if (i.type === 'pie-statistic-active') {
      // 只针对 start 阶段的配置，进行添加参数信息
      let startStages = [];
      if (!i.cfg?.start) {
        startStages = [
          {
            trigger: 'element:mouseenter',
            action: `${PIE_STATISTIC}:change`,
            arg: { statistic, annotations },
          },
        ];
      }
      each(i.cfg?.start, (stage) => {
        startStages.push({ ...stage, arg: { statistic, annotations } });
      });
      chart.interaction(i.type, deepAssign({}, i.cfg, { start: startStages }));
    } else {
      chart.interaction(i.type, i.cfg || {});
    }
  });

  return params;
}

/**
 * 饼图适配器
 * @param chart
 * @param options
 */
export function adaptor(params: Params<PieOptions>) {
  // flow 的方式处理所有的配置到 G2 API
  return flow<Params<PieOptions>>(
    pattern('pieStyle'),
    geometry,
    meta,
    theme,
    coordinate,
    legend,
    tooltip,
    label,
    state,
    /** 指标卡中心文本 放在下层 */
    pieAnnotation,
    interaction,
    animation
  )(params);
}
