// Copyright (c) 2022 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import React, {Component, ComponentType} from 'react';
import styled from 'styled-components';
import {createSelector} from 'reselect';

import ItemSelector from './item-selector/item-selector';
import {classList} from './item-selector/dropdown-list';
import {toArray} from 'utils/utils';
import {notNullorUndefined} from 'utils/data-utils';
import FieldTokenFactory from '../common/field-token';
import {Field} from 'utils/table-utils/kepler-table';
import {FIELD_TYPES_AGGREGATORS, FIELD_AGGREGATORS} from 'constants/default-settings';

const defaultDisplayOption = d => {
  const optionName = d.displayName || d.name;
  const aggregator = d.aggregator;
  const aggegatorDisplay = FIELD_AGGREGATORS.find(a => a.id === aggregator)?.displayName;
  if (aggegatorDisplay) {
    return `${aggegatorDisplay}(${optionName})`;
  }
  return optionName;
};
const defaultValueOption = d => d.name;

const StyledToken = styled.div`
  display: inline-block;
  margin: 0 0 0 auto;
`;
const StyledFieldListItem = styled.div`
  display: flex;
  flex: 1 1 auto;
  margin-right: 10px;
  line-height: 0;
  align-items: center;
`;

FieldListItemFactoryFactory.deps = [FieldTokenFactory];
// custom list Item
export function FieldListItemFactoryFactory(FieldToken) {
  const FieldListItemFactory = (showToken = true) => {
    const FieldListItem = ({value, displayOption = defaultDisplayOption}) => {
      return (
        <StyledFieldListItem className="field-selector_list-item">
          <span className={classList.listItemAnchor}>{displayOption(value)}</span>
          {showToken && value.type ? (
            <StyledToken>
              <FieldToken type={value.type} />
            </StyledToken>
          ) : null}
        </StyledFieldListItem>
      );
    };
    return FieldListItem;
  };
  return FieldListItemFactory;
}

const SuggestedFieldHeader = () => <div>Suggested Field</div>;

type FieldType =
  | string
  | string[]
  | {
      name: string;
      format: string | null;
    }[]
  | {
      format?: string;
      id?: string;
      name?: string;
      fieldIdx?: number;
      type?: number;
    }
  | Field;

interface FieldSelectorFactoryProps {
  fields?: FieldType[];
  onSelect: (
    items:
      | ReadonlyArray<string | number | boolean | object>
      | string
      | number
      | boolean
      | object
      | null
  ) => void;
  placement?: string;
  value?: FieldType | null;
  filterFieldTypes?: FieldType | FieldType[];
  inputTheme?: string;
  placeholder?: string;
  erasable?: boolean;
  error?: boolean;
  multiSelect?: boolean;
  closeOnSelect?: boolean;
  showToken?: boolean;
  suggested?: ReadonlyArray<string | number | boolean | object> | null;
  CustomChickletComponent?: ComponentType;
  size?: string;
  isLoading?: boolean;
  aggregator?: any,
  withAggregator?: any
  onBlur?: any,
  onFocus?: any,
}

function FieldSelectorFactory(
  FieldListItemFactory: ReturnType<typeof FieldListItemFactoryFactory>
): ComponentType<FieldSelectorFactoryProps> {
  class FieldSelector extends Component<FieldSelectorFactoryProps> {
    static defaultProps = {
      erasable: true,
      error: false,
      fields: [],
      onSelect: () => {},
      placement: 'bottom',
      value: null,
      aggregator: null,
      withAggregator: false,
      multiSelect: false,
      closeOnSelect: true,
      showToken: true,
      placeholder: 'placeholder.selectField'
    };

    fieldsSelector = props => props.fields;
    filteredFieldsSelector = props =>
      props.withAggregator
        ? props.fields
        : props.fields.filter(
            field =>
              !toArray(props.value).find(d => (d.name ? d.name === field.name : d === field.name))
          );
    valueSelector = props => props.value;
    aggregatorSelector = props => props.aggregator;
    filterFieldTypesSelector = props => props.filterFieldTypes;
    showTokenSelector = props => props.showToken;
    withAggregatorSelector = props => props.withAggregator;

    selectedItemsSelector = createSelector(
      this.fieldsSelector,
      this.valueSelector,
      this.aggregatorSelector,
      (fields, value, aggregator) =>
        toArray(value)
          .map(d => {
            const field = fields.find(f =>
              notNullorUndefined(d) && d.name
                ? d.name === defaultValueOption(f)
                : d === defaultValueOption(f)
            );

            if (field && aggregator) {
              return {...field, aggregator};
            }
            return field;
          })
          .filter(d => d)
    );

    fieldOptionsSelector = createSelector(
      this.filteredFieldsSelector,
      this.valueSelector,
      this.filterFieldTypesSelector,
      this.aggregatorSelector,
      this.withAggregatorSelector,
      (fields, value, filterFieldTypes, aggregator, withAggregator) => {
        const fieldsWithAggregators = fields.map(field => {
          return {
            ...field,
            ...(withAggregator && {
              aggregators: FIELD_TYPES_AGGREGATORS[field.type]?.filter(agg => {
                return field.id === value ? agg !== aggregator : true;
              })
            })
          };
        });
        if (!filterFieldTypes) {
          return fieldsWithAggregators;
        }
        const filters = Array.isArray(filterFieldTypes) ? filterFieldTypes : [filterFieldTypes];
        return fieldsWithAggregators.filter(f => filters.includes(f.type));
      }
    );

    fieldListItemSelector = createSelector(this.showTokenSelector, FieldListItemFactory);

    render() {
      return (
        <div className="field-selector">
          <ItemSelector
            getOptionValue={d => d}
            closeOnSelect={this.props.closeOnSelect}
            displayOption={defaultDisplayOption}
            filterOption="displayName"
            fixedOptions={this.props.suggested}
            inputTheme={this.props.inputTheme}
            size={this.props.size}
            isError={this.props.error}
            isLoading={this.props.isLoading}
            selectedItems={this.selectedItemsSelector(this.props)}
            erasable={this.props.erasable}
            options={this.fieldOptionsSelector(this.props)}
            multiSelect={this.props.multiSelect}
            placeholder={this.props.placeholder}
            placement={this.props.placement}
            onChange={this.props.onSelect}
            DropDownLineItemRenderComponent={this.fieldListItemSelector(this.props)}
            DropdownHeaderComponent={this.props.suggested ? SuggestedFieldHeader : null}
            CustomChickletComponent={this.props.CustomChickletComponent}
            withSubOptions={this.props.withAggregator}
            onBlur={this.props.onBlur}
            onFocus={this.props.onFocus}
          />
        </div>
      );
    }
  }
  return FieldSelector;
}

FieldSelectorFactory.deps = [FieldListItemFactoryFactory];
export default FieldSelectorFactory;
