import React from 'react';
import intl from 'react-intl-universal';
import { Chart } from '@antv/g2';
import { uniq } from 'lodash';
import { Spin } from 'antd';
import { connect } from 'react-redux';
import MachineDetail from '@/components/MachineDetail';
import LineChart from '@/components/Charts/LineChart';
import {
  CARD_POLLING_INTERVAL,
  DETAIL_DEFAULT_RANGE,
  getBaseLineByUnit,
  getDataByType,
  getMaxNum,
  getProperTickInterval,
} from '@/utils/dashboard';
import { configDetailChart, updateDetailChart } from '@/utils/chart/chart';
import { IStatRangeItem } from '@/utils/interface';
import { IDispatch, IRootState } from '@/store';
import './index.less';
import { SUPPORT_METRICS, VALUE_TYPE } from '@/utils/promQL';
import { trackEvent } from '@/utils/stat';
import Modal from '@/components/Modal';
import BaseLineEdit from '@/components/BaseLineEdit';

const mapDispatch = (dispatch: IDispatch) => ({
  asyncUpdateBaseLine: (key, value) =>
    dispatch.setting.update({
      [key]: value,
    }),
});

const mapState = (state: IRootState) => ({
  aliasConfig: state.app.aliasConfig,
  cpuBaseLine: state.setting.cpuBaseLine,
  memoryBaseLine: state.setting.memoryBaseLine,
  loadBaseLine: state.setting.loadBaseLine,
  diskBaseLine: state.setting.diskBaseLine,
  networkBaseLine: state.setting.networkBaseLine,
});
interface IProps
  extends ReturnType<typeof mapState>,
  ReturnType<typeof mapDispatch> {
  type: string;
  asyncGetDataSourceByRange: (params: {
    start: number;
    end: number;
    metric: string;
  }) => void;
  dataSource: IStatRangeItem[];
  metricOptions: {
    metric: string;
    valueType: VALUE_TYPE;
  }[];
  loading: true;
}

interface IState {
  maxNum: number;
  startTimestamps: number;
  endTimestamps: number;
  currentInstance: string;
  currentMetricOption: typeof SUPPORT_METRICS.cpu[0];
}

class Detail extends React.Component<IProps, IState> {
  pollingTimer: any;

  chartInstance: Chart;

  modalHandler;

  constructor(props: IProps) {
    super(props);
    const endTimestamps = Date.now();
    this.state = {
      maxNum: 0,
      endTimestamps,
      startTimestamps: endTimestamps - DETAIL_DEFAULT_RANGE,
      currentInstance: 'all',
      currentMetricOption: props.metricOptions[0],
    };
  }

  componentDidMount() {
    this.pollingData();
  }

  componentWillUnmount() {
    if (this.pollingTimer) {
      clearTimeout(this.pollingTimer);
    }
  }

  getData = async () => {
    const { startTimestamps, endTimestamps, currentMetricOption } = this.state;
    await this.props.asyncGetDataSourceByRange({
      start: startTimestamps,
      end: endTimestamps,
      metric: currentMetricOption.metric,
    });
    this.updateChart();
  };

  pollingData = () => {
    this.getData();
    this.pollingTimer = setTimeout(this.pollingData, CARD_POLLING_INTERVAL);
  };

  handleIntervalChange = (startTimestamps, endTimestamps) => {
    const { type } = this.props;
    trackEvent(`${type}_detail`, 'select_interval', `from_${type}_detail`);
    this.setState(
      {
        startTimestamps,
        endTimestamps,
      },
      this.getData,
    );
  };

  handleInstanceChange = instance => {
    const { type } = this.props;
    this.setState(
      {
        currentInstance: instance,
      },
      this.updateChart,
    );
    trackEvent(`${type}_detail`, 'select_data_type', `from_${type}_detail`);
  };

  handleMetricChange = async metric => {
    const { metricOptions, type, asyncUpdateBaseLine } = this.props;
    const metricOption = metricOptions.find(option => option.metric === metric);
    trackEvent(`${type}_detail`, 'select_metric_query', `from_${type}_detail`);
    await asyncUpdateBaseLine(`${type}BaseLine`, undefined);
    if (metricOption) {
      this.setState(
        {
          currentMetricOption: metricOption,
        },
        this.getData,
      );
    }
  };

  handleBaseLineChange = async value => {
    const { type, asyncUpdateBaseLine } = this.props;
    const { currentMetricOption } = this.state;
    const { baseLine, unit } = value;
    await asyncUpdateBaseLine(
      `${type}BaseLine`,
      getBaseLineByUnit({
        baseLine,
        unit,
        valueType: currentMetricOption.valueType,
      }),
    );
    this.modalHandler.hide();
  };

  renderChart = (chartInstance: Chart) => {
    const { currentMetricOption } = this.state;
    const { startTimestamps, endTimestamps } = this.state;
    this.chartInstance = chartInstance;
    configDetailChart(chartInstance, {
      tickInterval: getProperTickInterval(endTimestamps - startTimestamps),
      valueType: currentMetricOption.valueType,
    });
  };

  updateChart = () => {
    const { dataSource, type, aliasConfig } = this.props;
    const { currentInstance, startTimestamps, endTimestamps } = this.state;
    const data = getDataByType({
      data: dataSource,
      type: currentInstance,
      name: 'instance',
      aliasConfig,
    });
    this.setState({
      maxNum: getMaxNum(data),
    });
    updateDetailChart(this.chartInstance, {
      type,
      tickInterval: getProperTickInterval(endTimestamps - startTimestamps),
    }).changeData(data);
    this.chartInstance.autoFit = true;
  };

  handleBaseLineEdit = () => {
    if (this.modalHandler) {
      this.modalHandler.show();
    }
  };

  handleClose = () => {
    if (this.modalHandler) {
      this.modalHandler.hide();
    }
  };

  render() {
    const {
      maxNum,
      startTimestamps,
      endTimestamps,
      currentInstance,
      currentMetricOption,
    } = this.state;
    const { dataSource, metricOptions, loading, aliasConfig, type } =
      this.props;
    const instances = uniq(
      dataSource.map(instance => instance.metric.instance),
    );
    const typeOptions = [
      {
        name: intl.get('device.detail.all'),
        value: 'all',
      },
      ...instances.map(instance => ({
        name: aliasConfig[instance] || instance,
        value: instance,
      })),
    ];
    const baseLine = this.props[`${type}BaseLine`];
    return (
      <Spin spinning={loading} wrapperClassName="machine-detail">
        <MachineDetail
          key={currentMetricOption.metric}
          className="cpu-detail"
          title={intl.get('device.detail.cpu')}
          onTimeChange={this.handleIntervalChange}
          startTimestamps={startTimestamps}
          endTimestamps={endTimestamps}
          typeOptions={typeOptions}
          currentType={currentInstance}
          metricOptions={metricOptions}
          onTypeChange={this.handleInstanceChange}
          currentMetricOption={currentMetricOption}
          onMetricChange={this.handleMetricChange}
          onBaseLineEdit={this.handleBaseLineEdit}
        >
          <LineChart
            isDefaultScale={
              currentMetricOption.valueType === VALUE_TYPE.percentage
            } // VALUE_TYPE.percentage has a default Scale
            yAxisMaximum={maxNum}
            tickInterval={getProperTickInterval(
              endTimestamps - startTimestamps,
            )}
            baseLine={baseLine}
            options={{ padding: [10, 70, 70, 70] }}
            renderChart={this.renderChart}
          />
        </MachineDetail>
        <Modal
          title="empty"
          className="machine-modal"
          width="550px"
          handlerRef={handler => (this.modalHandler = handler)}
          footer={null}
        >
          <BaseLineEdit
            valueType={currentMetricOption.valueType}
            baseLine={baseLine || 0}
            onClose={this.handleClose}
            onBaseLineChange={this.handleBaseLineChange}
          />
        </Modal>
      </Spin>
    );
  }
}

export default connect(mapState, mapDispatch)(Detail);