import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import classNames from 'clsx';

import { iabClassificationCategoriesCollection, numberInRange } from '../../helpers';
import { modelBlockInfoComputedSelector } from '../../redux/selectors';
import {
  onAddFormAction,
  onChangeModelFormAction,
  onChangeModelTempFormAction,
  onDeleteFormAction,
  onUpdateFormAction,
} from '../../redux/slices';

import { Button, CircularProgress, MenuItem, Paper, Select, Stack, TextField, Typography } from '@/mui/components';

import styles from './Models.module.scss';

const orderItems = ['model', 'category', 'lowerBound', 'maxResults'];

const getFormCollectionData = (providers, models) => {
  const { providerName, formList, tempForm } = models;
  const providerModels = providers.find(($el) => $el.provider === providerName)?.models ?? [];

  return {
    providerName,
    formsList: formList.map((form) => ({
      formId: form.formId,
      model: {
        options: providerModels.map((item) => ({ ...item })),
        selected: form.model,
      },
      category: {
        options: iabClassificationCategoriesCollection.map((item) => ({ ...item })),
        selected: form.category,
      },
      lowerBound: {
        value: form.lowerBound,
      },
      maxResults: {
        value: form.maxResults,
      },
    })),
    tempForm,
  };
};

const returnRowTitle = () => (
  <div className={classNames(styles.Row, styles.Row___title)} key="table-title">
    <div className={styles.Cell}>
      <Typography variant="subtitle1">Model</Typography>
    </div>
    <div className={styles.Cell}>
      <Typography variant="subtitle1">Category</Typography>
    </div>
    <div className={styles.Cell}>
      <Typography variant="subtitle1">Lower bound</Typography>
    </div>
    <div className={styles.Cell}>
      <Typography variant="subtitle1">Max results</Typography>
    </div>
    <div className={styles.Cell}>
      <Typography variant="subtitle1">Actions</Typography>
    </div>
  </div>
);

function Models() {
  const dispatch = useDispatch();
  const modelBlockInfo = useSelector(modelBlockInfoComputedSelector);

  const amazonFormCollection = getFormCollectionData(modelBlockInfo.providers, modelBlockInfo.awsRecognitionModels);
  const clarifaiFormCollection = getFormCollectionData(modelBlockInfo.providers, modelBlockInfo.clarifaiModels);
  const googleFormCollection = getFormCollectionData(modelBlockInfo.providers, modelBlockInfo.googleModels);
  const weavoFormCollection = getFormCollectionData(modelBlockInfo.providers, modelBlockInfo.weavoModels);

  const returnRows = ({ providerName, formsList, tempForm }) => {
    const onBlurInput = ({ formId, fieldName, min, max, isFloat, value }) => {
      const resultValue = numberInRange({
        min,
        max,
        isFloat,
        value,
      });

      let result = resultValue;

      if (Number.isNaN(resultValue)) {
        result = fieldName === 'maxResults' ? null : min;
      }

      dispatch(
        onChangeModelFormAction({
          fieldName,
          providerName,
          formId,
          value: result,
        }),
      );
    };

    const onChangeInput = ({ formId, fieldName, min, max, isFloat, value }) => {
      const resultValue = numberInRange({
        min,
        max,
        isFloat,
        value,
      });

      dispatch(
        onChangeModelFormAction({
          fieldName,
          providerName,
          formId,
          value: Number.isNaN(resultValue) ? null : resultValue,
        }),
      );
    };

    const onChangeSelect = (formId, fieldName, event) => {
      const targetForm = formsList.find((form) => form.formId === formId);
      const { options } = targetForm[fieldName];

      dispatch(
        onChangeModelFormAction({
          fieldName,
          providerName,
          formId,
          value: options[event.target.selectedIndex].value,
        }),
      );
    };

    const onChangeEditInput = ({ fieldName, value, min, max, isFloat }) => {
      const resultValue = numberInRange({
        min,
        max,
        isFloat,
        value,
      });

      dispatch(
        onChangeModelTempFormAction({
          fieldName,
          providerName,
          value: {
            value: Number.isNaN(resultValue) ? null : resultValue,
          },
        }),
      );
    };

    const onBlurEditInput = ({ fieldName, value, min, max, isFloat }) => {
      const resultValue = numberInRange({
        min,
        max,
        isFloat,
        value,
      });

      let result = resultValue;

      if (Number.isNaN(resultValue)) {
        result = fieldName === 'maxResults' ? null : min;
      }

      dispatch(
        onChangeModelTempFormAction({
          fieldName,
          providerName,
          value: {
            value: result,
          },
        }),
      );
    };

    const onChangeEditSelect = (fieldName, event) => {
      const { options } = tempForm[fieldName];

      dispatch(
        onChangeModelTempFormAction({
          fieldName,
          providerName,
          value: {
            selected: options[event.target.selectedIndex].value,
          },
        }),
      );
    };

    const formComponents = formsList.map((dataRow) => {
      const { formId } = dataRow;

      const rowElement = orderItems.map((fieldName) => {
        const oneFieldInfo = dataRow[fieldName];

        let input;

        if (fieldName === 'model' || fieldName === 'category') {
          input = (
            <Select
              data-id={`select-${providerName}-${fieldName}`}
              key={`${providerName}-${formId}-${fieldName}-select`}
              defaultValue={oneFieldInfo.selected}
              shape="square"
              size="small"
              onChange={(event) => onChangeSelect(formId, fieldName, event)}
            >
              {oneFieldInfo.options.map(($one) => (
                <MenuItem key={`options-${$one.value}`} value={$one.value}>
                  {$one.name}
                </MenuItem>
              ))}
            </Select>
          );
        } else {
          const isFloatInput = fieldName === 'lowerBound';
          const minInput = 0;
          const stepInput = isFloatInput ? 0.01 : 1;
          const maxInput = isFloatInput ? 1 : 100;

          input = (
            <TextField
              data-id={`input-${providerName}-${fieldName}`}
              key={`${providerName}-${formId}-${fieldName}-input`}
              type="number"
              min={minInput}
              step={stepInput}
              max={maxInput}
              shape="square"
              size="small"
              className={styles.Input}
              value={oneFieldInfo.value ?? ''}
              onChange={(event) =>
                onChangeInput({
                  min: minInput,
                  max: maxInput,
                  isFloat: isFloatInput,
                  formId,
                  fieldName,
                  value: event.target.value,
                })
              }
              onBlur={(event) =>
                onBlurInput({
                  min: minInput,
                  max: maxInput,
                  isFloat: isFloatInput,
                  formId,
                  fieldName,
                  value: event.target.value,
                })
              }
            />
          );
        }

        return (
          <div
            className={classNames(styles.Cell, {
              [styles.Cell___big]: fieldName === 'model' || fieldName === 'category',
            })}
            key={`${providerName}-${formId}-${fieldName}-cell`}
          >
            {input}
          </div>
        );
      });

      rowElement.push(
        <div className={styles.Cell} key={`${providerName}-${formId}-button-cell`}>
          <Stack spacing={2} alignItems="center" direction="row">
            <Button
              data-id={`button-update-${providerName}`}
              size="small"
              onClick={() =>
                dispatch(
                  onUpdateFormAction({
                    providerName,
                  }),
                )
              }
            >
              UPDATE
            </Button>
            <Button
              data-id={`button-delete-${providerName}`}
              color="error"
              size="small"
              onClick={() =>
                dispatch(
                  onDeleteFormAction({
                    providerName,
                    formId,
                  }),
                )
              }
            >
              DELETE
            </Button>
          </Stack>
        </div>,
      );

      return (
        <div className={styles.Row} data-form-id={formId} key={`${providerName}-${formId}-row`}>
          {rowElement}
        </div>
      );
    });

    if (tempForm) {
      formComponents.push(
        <div className={styles.Row} key={`${providerName}-tempForm-row`}>
          <div className={styles.Cell}>
            <Select
              data-id={`select-temp-form-${providerName}-model`}
              className={styles.Input}
              shape="square"
              size="small"
              defaultValue={tempForm.model.selected}
              onChange={(event) => onChangeEditSelect('model', event)}
            >
              {tempForm.model.options.map(($one) => (
                <MenuItem key={`options-${$one.value}`} value={$one.value}>
                  {$one.name}
                </MenuItem>
              ))}
            </Select>
          </div>
          <div className={styles.Cell}>
            <Select
              data-id={`select-temp-form-${providerName}-category`}
              className={styles.Input}
              shape="square"
              size="small"
              onChange={(event) => onChangeEditSelect('category', event)}
              defaultValue={tempForm.category.selected}
            >
              {tempForm.category.options.map(($one) => (
                <MenuItem key={`${providerName}-tempForm-options-${$one.value}`} value={$one.value}>
                  {$one.name}
                </MenuItem>
              ))}
            </Select>
          </div>
          <div className={styles.Cell}>
            <TextField
              data-id={`input-temp-form-${providerName}-lowerBound`}
              type="number"
              min="0"
              step="0.01"
              max="1"
              shape="square"
              c
              size="small"
              className={styles.Input}
              value={tempForm.lowerBound.value ?? ''}
              onChange={(event) =>
                onChangeEditInput({
                  fieldName: 'lowerBound',
                  value: event.target.value,
                  min: 0,
                  max: 1,
                  isFloat: true,
                })
              }
              onBlur={(event) =>
                onBlurEditInput({
                  fieldName: 'lowerBound',
                  value: event.target.value,
                  min: 0,
                  max: 1,
                  isFloat: true,
                })
              }
            />
          </div>
          <div className={styles.Cell}>
            <TextField
              data-id={`input-temp-form-${providerName}-maxResults`}
              type="number"
              min="0"
              step="1"
              max="100"
              shape="square"
              size="small"
              className={styles.Input}
              value={tempForm.maxResults.value ?? ''}
              onChange={(event) =>
                onChangeEditInput({
                  fieldName: 'maxResults',
                  value: event.target.value,
                  min: 0,
                  max: 100,
                  isFloat: false,
                })
              }
              onBlur={(event) =>
                onBlurEditInput({
                  fieldName: 'maxResults',
                  value: event.target.value,
                  min: 0,
                  max: 100,
                  isFloat: false,
                })
              }
            />
          </div>
          <div className={styles.Cell}>
            <Button
              data-id={`button-add-temp-form-${providerName}`}
              size="small"
              onClick={() =>
                dispatch(
                  onAddFormAction({
                    providerName,
                  }),
                )
              }
            >
              ADD
            </Button>
          </div>
        </div>,
      );
    }

    return formComponents;
  };

  return (
    <Stack spacing={4} className={styles.Wrapper}>
      {modelBlockInfo.isLoading && (
        <div className={styles.SpinnerWrapper}>
          <CircularProgress size="large" />
        </div>
      )}
      <Paper elevation={1} className={styles.BlockContentAnalytics}>
        <Stack spacing={2}>
          <Typography variant="h6Alt" align="center">
            AWS Models
          </Typography>
          <div className={styles.BlockTable}>
            {returnRowTitle()}
            {returnRows(amazonFormCollection)}
          </div>
        </Stack>
      </Paper>
      <Paper elevation={1} className={styles.BlockContentAnalytics}>
        <Stack spacing={2}>
          <Typography variant="h6Alt" align="center">
            Clarifai Models
          </Typography>
          <div className={styles.BlockTable}>
            {returnRowTitle()}
            {returnRows(clarifaiFormCollection)}
          </div>
        </Stack>
      </Paper>
      <Paper elevation={1} className={styles.BlockContentAnalytics}>
        <Stack spacing={2}>
          <Typography variant="h6Alt" align="center">
            Google Models
          </Typography>
          <div className={styles.BlockTable}>
            {returnRowTitle()}
            {returnRows(googleFormCollection)}
          </div>
        </Stack>
      </Paper>
      <Paper elevation={1} className={styles.BlockContentAnalytics}>
        <Stack spacing={2}>
          <Typography variant="h6Alt" align="center">
            WEAVO Models
          </Typography>
          <div className={styles.BlockTable}>
            {returnRowTitle()}
            {returnRows(weavoFormCollection)}
          </div>
        </Stack>
      </Paper>
    </Stack>
  );
}

export default Models;
