/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import defaults from 'lodash/defaults';
import React, { ChangeEvent, PureComponent } from 'react';
import { QueryEditorProps, SelectableValue } from '@grafana/data';
import { DataSource } from './datasource';
import { GroupBy, IoTDBOptions, IoTDBQuery } from './types';
import { QueryField, QueryInlineField } from './componments/Form';
import { TimeSeries } from './componments/TimeSeries';
import { SelectValue } from './componments/SelectValue';
import { FromValue } from './componments/FromValue';
import { WhereValue } from './componments/WhereValue';
import { ControlValue } from './componments/ControlValue';
import { FillValue } from './componments/FillValue';
import { Segment } from '@grafana/ui';
import { toOption } from './functions';

import { GroupByLabel } from './componments/GroupBy';
import { AggregateFun } from './componments/AggregateFun';

interface State {
  expression: string[];
  prefixPath: string[];
  condition: string;
  control: string;

  timeSeries: string[];
  options: Array<Array<SelectableValue<string>>>;
  aggregateFun: string;
  groupBy: GroupBy;
  fillClauses: string;
  isDropDownList: boolean;
  sqlType: string;
  shouldAdd: boolean;
}

const selectElement = [
  '---remove---',
  'SUM',
  'COUNT',
  'AVG',
  'EXTREME',
  'MAX_VALUE',
  'MIN_VALUE',
  'FIRST_VALUE',
  'LAST_VALUE',
  'MAX_TIME',
  'MIN_TIME',
];

const paths = [''];
const expressions = [''];
const selectType = ['SQL: Full Customized', 'SQL: Drop-down List'];
const commonOption: SelectableValue<string> = { label: '*', value: '*' };
const commonOptionDou: SelectableValue<string> = { label: '**', value: '**' };
type Props = QueryEditorProps<DataSource, IoTDBQuery, IoTDBOptions>;

export class QueryEditor extends PureComponent<Props, State> {
  state: State = {
    expression: expressions,
    prefixPath: paths,
    condition: '',
    control: '',
    timeSeries: [],
    options: [[toOption('')]],
    aggregateFun: '',
    groupBy: {
      samplingInterval: '',
      step: '',
      groupByLevel: '',
    },
    fillClauses: '',
    isDropDownList: false,
    sqlType: selectType[0],
    shouldAdd: true,
  };

  onSelectValueChange = (exp: string[]) => {
    const { onChange, query } = this.props;
    this.setState({ expression: exp });
    onChange({ ...query, expression: exp });
  };

  onFromValueChange = (p: string[]) => {
    const { onChange, query } = this.props;
    this.setState({ prefixPath: p });
    onChange({ ...query, prefixPath: p });
  };

  onWhereValueChange = (c: string) => {
    const { onChange, query } = this.props;
    onChange({ ...query, condition: c });
    this.setState({ condition: c });
  };
  onControlValueChange = (c: string) => {
    const { onChange, query } = this.props;
    onChange({ ...query, control: c });
    this.setState({ control: c });
  };

  onAggregationsChange = (a: string) => {
    const { onChange, query } = this.props;
    if (a === '---remove---') {
      a = '';
    }
    this.setState({ aggregateFun: a });
    onChange({ ...query, aggregateFun: a });
  };

  onFillsChange = (f: string) => {
    const { onChange, query } = this.props;
    onChange({ ...query, fillClauses: f });
    this.setState({ fillClauses: f });
  };

  onGroupByChange = (g: GroupBy) => {
    const { onChange, query } = this.props;
    this.setState({ groupBy: g });
    onChange({ ...query, groupBy: g });
  };

  onQueryTextChange = (event: ChangeEvent<HTMLInputElement>) => {
    const { onChange, query } = this.props;
    onChange({ ...query });
  };

  onSelectTypeChange = (event: ChangeEvent<HTMLInputElement>) => {
    const { onChange, query } = this.props;
    onChange({ ...query });
  };

  onTimeSeriesChange = (t: string[], options: Array<Array<SelectableValue<string>>>, isRemove: boolean) => {
    const { onChange, query } = this.props;
    const commonOption: SelectableValue<string> = { label: '*', value: '*' };
    if (t.length === options.length) {
      this.props.datasource
        .nodeQuery(['root', ...t])
        .then((a) => {
          let b = a.map((a) => a.text).map(toOption);
          if (b.length > 0) {
            b = [commonOption, commonOptionDou, ...b];
          }
          onChange({ ...query, paths: t, options: [...options, b] });
          if (isRemove) {
            this.setState({ timeSeries: t, options: [...options, b], shouldAdd: true });
          } else {
            this.setState({ timeSeries: t, options: [...options, b] });
          }
        })
        .catch((e) => {
          if (e === 'measurement') {
            onChange({ ...query, paths: t });
            this.setState({ timeSeries: t, shouldAdd: false });
          } else {
            this.setState({ shouldAdd: false });
          }
        });
    } else {
      this.setState({ timeSeries: t });
      onChange({ ...query, paths: t });
    }
  };

  componentDidMount() {
    if (this.props.query.sqlType) {
      this.setState({ isDropDownList: this.props.query.isDropDownList, sqlType: this.props.query.sqlType });
    } else {
      this.props.query.sqlType = selectType[0];
    }
    if (this.state.options.length === 1 && this.state.options[0][0].value === '') {
      this.props.datasource.nodeQuery(['root']).then((a) => {
        let b = a.map((a) => a.text).map(toOption);
        if (b.length > 0) {
          b = [commonOption, commonOptionDou, ...b];
        }
        this.setState({ options: [b] });
      });
    }
  }

  render() {
    const query = defaults(this.props.query);
    var { expression, prefixPath, condition, control, fillClauses, aggregateFun, paths, options, sqlType, groupBy } =
      query;
    return (
      <>
        {
          <>
            <div className="gf-form">
              <Segment
                onChange={({ value: value = '' }) => {
                  const { onChange, query } = this.props;
                  if (value === selectType[0]) {
                    this.props.query.sqlType = selectType[0];
                    this.props.query.aggregateFun = '';
                    if (this.props.query.paths) {
                      const nextTimeSeries = this.props.query.paths.filter((_, i) => i < 0);
                      const nextOptions = this.props.query.options.filter((_, i) => i < 0);
                      this.onTimeSeriesChange(nextTimeSeries, nextOptions, true);
                    }
                    if (this.props.query.groupBy?.samplingInterval) {
                      this.props.query.groupBy.samplingInterval = '';
                    }
                    if (this.props.query.groupBy?.groupByLevel) {
                      this.props.query.groupBy.groupByLevel = '';
                    }
                    if (this.props.query.groupBy?.step) {
                      this.props.query.groupBy.step = '';
                    }
                    this.props.query.condition = '';
                    this.props.query.fillClauses = '';
                    this.props.query.isDropDownList = false;
                    this.setState({
                      isDropDownList: false,
                      sqlType: selectType[0],
                      shouldAdd: true,
                      aggregateFun: '',
                      fillClauses: '',
                      condition: '',
                    });
                    onChange({ ...query, sqlType: value, isDropDownList: false });
                  } else {
                    this.props.query.sqlType = selectType[1];
                    this.props.query.expression = [''];
                    this.props.query.prefixPath = [''];
                    this.props.query.condition = '';
                    this.props.query.control = '';
                    this.props.query.isDropDownList = true;
                    this.setState({
                      isDropDownList: true,
                      sqlType: selectType[1],
                      expression: [''],
                      prefixPath: [''],
                      condition: '',
                      control: '',
                    });
                    onChange({ ...query, sqlType: value, isDropDownList: true });
                  }
                }}
                options={selectType.map(toOption)}
                value={sqlType ? sqlType : this.state.sqlType}
                className="query-keyword width-10"
              />
            </div>
            {!this.state.isDropDownList && (
              <>
                <div className="gf-form">
                  <QueryInlineField label={'SELECT'}>
                    <SelectValue
                      expressions={expression ? expression : this.state.expression}
                      onChange={this.onSelectValueChange}
                    />
                  </QueryInlineField>
                </div>
                <div className="gf-form">
                  <QueryInlineField label={'FROM'}>
                    <FromValue
                      prefixPath={prefixPath ? prefixPath : this.state.prefixPath}
                      onChange={this.onFromValueChange}
                    />
                  </QueryInlineField>
                </div>
                <div className="gf-form">
                  <QueryInlineField label={'WHERE'}>
                    <WhereValue
                      condition={condition ? condition : this.state.condition}
                      onChange={this.onWhereValueChange}
                    />
                  </QueryInlineField>
                </div>
                <div className="gf-form">
                  <QueryInlineField label={'CONTROL'}>
                    <ControlValue
                      control={control ? control : this.state.control}
                      onChange={this.onControlValueChange}
                    />
                  </QueryInlineField>
                </div>
              </>
            )}
            {this.state.isDropDownList && (
              <>
                <div className="gf-form">
                  <QueryInlineField label={'TIME-SERIES'}>
                    <TimeSeries
                      timeSeries={paths ? paths : this.state.timeSeries}
                      onChange={this.onTimeSeriesChange}
                      variableOptionGroup={options ? options : this.state.options}
                      shouldAdd={this.state.shouldAdd}
                    />
                  </QueryInlineField>
                </div>
                <div className="gf-form">
                  <QueryInlineField label={'FUNCTION'}>
                    <AggregateFun
                      aggregateFun={aggregateFun ? aggregateFun : this.state.aggregateFun}
                      onChange={this.onAggregationsChange}
                      variableOptionGroup={selectElement.map(toOption)}
                    />
                  </QueryInlineField>
                </div>
                <div className="gf-form">
                  <QueryInlineField label={'WHERE'}>
                    <WhereValue
                      condition={condition ? condition : this.state.condition}
                      onChange={this.onWhereValueChange}
                    />
                  </QueryInlineField>
                </div>
                <div className="gf-form">
                  <QueryInlineField label={'GROUP BY'}>
                    <QueryField label={'SAMPLING INTERVAL'} />
                    <GroupByLabel groupBy={groupBy ? groupBy : this.state.groupBy} onChange={this.onGroupByChange} />
                  </QueryInlineField>
                </div>
                <div className="gf-form">
                  <QueryInlineField label={'FILL'}>
                    <FillValue
                      fill={fillClauses ? fillClauses : this.state.fillClauses}
                      onChange={this.onFillsChange}
                    />
                  </QueryInlineField>
                </div>
              </>
            )}
          </>
        }
      </>
    );
  }
}
