import { IOption } from '@rh-support/types';
import { toOption, toOptions } from '@rh-support/utils';
import difference from 'lodash/difference';
import find from 'lodash/find';
import findIndex from 'lodash/findIndex';
import includes from 'lodash/includes';
import isArray from 'lodash/isArray';
import isEmpty from 'lodash/isEmpty';
import map from 'lodash/map';
import React, { useContext, useEffect, useMemo, useState } from 'react';
import { RouteComponentProps } from 'react-router';
import { AppMetadataDispatchContext, AppMetadataStateContext } from '../../context/AppMetadataContext';
import { CreateCaseDispatchContext, CreateCaseStateContext } from '../../context/CreateCaseContext';
import { RouteContext, RouteDispatchContext } from '../../context/RouteContext';
import {
  IProduct,
  IProductResponse,
  IVersion,
  setAllProducts
} from '../../reducers/AppMetadataReducer';
import { initialCreateCaseState, setCaseCreationDetails } from '../../reducers/CreateCaseReducer';
import { AppRouteSections, IRouteQueryParams, IRouteUrlParams, RouteReducerConstants } from '../../reducers/RouteReducer';
import CaseUtils from '../../utils/caseUtils';
import RouteUtils from '../../utils/routeUtils';
import AlertMessage, { AlertType } from '../shared/AlertMessage';
import { Dropdown } from '../shared/input/DropdownList';
import { RadioList } from '../shared/input/RadioList';
import LoadingIndicator from '../shared/LoadingIndicator';
import { ProductVersionDropdownSelector } from './ProductVersionDropdownSelector';

interface IProps {
  routeProps: RouteComponentProps<IRouteUrlParams>;
}

export default function ProductSelector(props: IProps) {
  const defaultSelectedProduct = {
    featuredVersion: '',
    product_name: '',
    versions: [],
    selectedVersion: ''
  };
  const [selectedProductForDropdown, setSelectedProductForDropdown] = useState<IProduct>(defaultSelectedProduct);
  const [selectedRadioIndex, setSelectedRadioIndex] = React.useState<number>(-1);
  const { routeState: {
    activeSection,
    isSectionValid,
    appRouteSectionsSequence,
    sectionsToHide
  } } = useContext(RouteContext);
  const { appMetadataState: { allProducts, nextButtonRef } } = useContext(AppMetadataStateContext);
  const { createCaseState: { product, version } } = useContext(CreateCaseStateContext);
  const dispatchToRouteReducer = useContext(RouteDispatchContext);
  const dispatchToAppMetadataReducer = useContext(AppMetadataDispatchContext);
  const createCaseDispatch = useContext(CreateCaseDispatchContext);

  const onProductVersionSelect = (selectedProduct: string, selectedVersion: string) => {
    const queryParams: IRouteQueryParams = {};
    selectedProduct && (queryParams.product = selectedProduct);
    selectedVersion && (queryParams.version = selectedVersion);
    const queryString = CaseUtils.getStringifiedParams(queryParams);
    props.routeProps.history.replace({ pathname: `${RouteUtils.getRoutePathForActiveSection(AppRouteSections.PRODUCT_SELECTOR, appRouteSectionsSequence)}`, search: queryString });
  };

  const onAllProductsChange = (newAllProducts: IProductResponse) => {
    setAllProducts(dispatchToAppMetadataReducer, newAllProducts);
  };

  // Product version from url related
  useEffect(() => {
    const parsedUrlParams = CaseUtils.getUrlParsedParams(props.routeProps.location.search);
    const productFromUrl = parsedUrlParams.product || initialCreateCaseState.product;
    const versionFromUrl = parsedUrlParams.version || initialCreateCaseState.version;
    const index = findIndex(allProducts.data.productsResult, (p: IProduct) => p.product_name === productFromUrl);

    if (index !== -1) { // product in url is valid
      if (versionFromUrl && allProducts.data.productsResult[index].versions.indexOf(versionFromUrl) !== -1) {
        setCaseCreationDetails(createCaseDispatch, { product: productFromUrl, version: versionFromUrl });
      } else {
        // no version or invalid version, we set default version in url that will trigger the flow with valid version this time
        const defaultVersion = allProducts.data.productsResult[index].selectedVersion;
        onProductVersionSelect(productFromUrl, defaultVersion);
      }
    } else {
      setCaseCreationDetails(createCaseDispatch, { product: productFromUrl, version: versionFromUrl });
    }
  }, [props.routeProps.location.search, allProducts.data]);

  // Validations effect
  useEffect(() => {
    if (activeSection !== AppRouteSections.PRODUCT_SELECTOR) { return; }
    const isSectionValidLocal = !isEmpty(product) && !isEmpty(version) && !allProducts.isFetching && !allProducts.isError;
    if (isSectionValid === isSectionValidLocal) { return; }
    dispatchToRouteReducer({ type: RouteReducerConstants.setIsSectionValid, payload: { isSectionValid: isSectionValidLocal } });
  }, [activeSection, product, version, allProducts.isFetching, allProducts.isError]);

  useEffect(() => {
    if (allProducts.isFetching || allProducts.isError) { return; }
    let index = findIndex(allProducts.data.topProducts, (p: IProduct) => p.product_name === product && p.selectedVersion === version);
    if (index === -1) {
      let selectedOtherProduct: IProduct = find(allProducts.data.otherProducts, (p: IProduct) => p.product_name === product && includes(p.versions, version));
      if (selectedOtherProduct) {
        selectedOtherProduct.selectedVersion = version;
        index = allProducts.data.topProducts.length;
      } else {
        index = selectedRadioIndex;
        selectedOtherProduct = defaultSelectedProduct;
      }
      setSelectedProductForDropdown(selectedOtherProduct);
    }
    if (selectedRadioIndex !== index) { setSelectedRadioIndex(index); }
  }, [product, version, allProducts.data]);

  // Showing suggestions section if product version changes
  useEffect(() => {
    if (sectionsToHide.indexOf(AppRouteSections.SUGGESTIONS) >  -1) {
      dispatchToRouteReducer({ type: RouteReducerConstants.updateSectionsToHide, payload: { sectionsToHide: difference(sectionsToHide, [AppRouteSections.SUGGESTIONS]) } });
    }
  }, [product, version]);

  function onProductChangeForOtherProducts(selectedProduct: IProduct) {
    onProductVersionSelect(selectedProduct.product_name, selectedProduct.selectedVersion);
  }

  function onVersionChangeForOtherProducts(selectedVersion: IVersion) {
    onProductVersionSelect(product, selectedVersion);
    const newOtherProducts = map(allProducts.data.otherProducts, (p) => {
      return p.product_name === product ? { ...p, selectedVersion } : p;
    });
    onAllProductsChange({ ...allProducts.data, otherProducts: newOtherProducts });
  }

  function onVersionChangeForTopProducts(option: IOption<IVersion>) {
    onProductVersionSelect(product, option.value);
    const newTopProducts = map(allProducts.data.topProducts, (p: IProduct) => {
      return p.product_name === product ? { ...p, selectedVersion: option.value } : p;
    });
    onAllProductsChange({ ...allProducts.data, topProducts: newTopProducts });
  }

  function renderItem(id: string, selectedProduct: IProduct | IProduct[], isCheckboxSelected: boolean) {
    if (isArray(selectedProduct)) {
      return <ProductVersionDropdownSelector
        htmlForId={id}
        disabled={!isCheckboxSelected}
        products={selectedProduct as IProduct[]}
        selectedProduct={selectedProductForDropdown}
        onProductChange={onProductChangeForOtherProducts}
        onVersionChange={onVersionChangeForOtherProducts}
      />;
    } else {
      const uniqueID = selectedProduct.product_name.replace(/\s+/g, '-').replace('.', '').toLowerCase();
      return (
        <>
          <label htmlFor={id}>{selectedProduct.product_name}</label>
          <Dropdown<IVersion>
            id={`${uniqueID}-version`}
            list={toOptions<IVersion>(selectedProduct.versions)}
            selectedItem={toOption<IVersion>(selectedProduct.selectedVersion)}
            title={`${selectedProduct.product_name} version dropdown`}
            disabled={!isCheckboxSelected}
            onChange={onVersionChangeForTopProducts}
          />
        </>
      );
    }
  }

  function onItemSelectFromRadioList(item: IProduct | IProduct[], index: number) {
    setSelectedRadioIndex(index);
    if (!isArray(item)) {
      const selectedProduct = item as IProduct;
      onProductVersionSelect(selectedProduct.product_name, selectedProduct.selectedVersion);
    } else {
      onProductVersionSelect(selectedProductForDropdown.product_name, selectedProductForDropdown.selectedVersion);
    }
  }

  const handleFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    // simulating click
    nextButtonRef.current && nextButtonRef.current.click();
};

  return useMemo(() => {
    return (<>
      <LoadingIndicator show={allProducts.isFetching} size="lg" />
      <AlertMessage type={AlertType.DANGER} className="push-top" message="There was an error loading products." show={allProducts.isError} />
      {!isEmpty(allProducts.data.topProducts) && !isEmpty(allProducts.data.otherProducts) &&
        <form onSubmit={handleFormSubmit}>
          <RadioList<IProduct | IProduct[]>
            id="product-selector"
            className="product-selector-list"
            selectedRadioIndex={selectedRadioIndex}
            listItems={[...allProducts.data.topProducts, allProducts.data.otherProducts]}
            renderItem={renderItem}
            onSelect={onItemSelectFromRadioList} />
        </form>
      }
    </>);
}, [props, product, version, allProducts, nextButtonRef, selectedProductForDropdown, selectedRadioIndex]);
}
