import React, { useEffect, useState, forwardRef, useImperativeHandle, useMemo } from 'react';
import {
  Box,
  Typography,
  TextField,
  Autocomplete,
  Chip,
  MenuItem,
  Select,
  OutlinedInput,
  FormHelperText,
  Tooltip
} from '@mui/material';
import { ExpandMore as ExpandMoreIcon, Help as HelpIcon } from '@mui/icons-material';
import { makeStyles } from 'tss-react/mui';
import useValidation, { defaultSubentriesRules } from '../../../hooks/useValidation';
import StringValue, { ValueView as StringViewValue } from './types/String';
import ListValue, { ValueView as ListViewValue } from './types/List';
import DataValue, { ValueView as DataViewValue } from './types/Data';
import { getFirstEntryName, getFirstEntryValue } from '../../../utils';
import { CmdbItem } from '../../../store/cmdbSlice/thunks';
import { CMDB_ITEM_TYPES } from '../../../store/constants';
import { getAutocompleteVariables } from '../../../store/cmdbSlice/thunks';
import { useAutocompleteField } from '../../../hooks/useAutocompleteField';

export const VARIABLE_VALUES_TYPES: VariableType[] = [
  {
    value: "string",
    label: "String",
    component: ({ setValue, errors, value }) => <StringValue onChange={setValue} errors={errors} value={value} />,
    viewValueComponent: ({ value }) => <StringViewValue value={value} />,
    validation: {}
  },
  {
    value: "list",
    label: "List",
    component: ({ setValue, errors, value }) => <ListValue onChange={setValue} errors={errors} value={value} />,
    viewValueComponent: ({ value }) => <ListViewValue value={value} />,
    validation: {
      item_value: {
        ...defaultSubentriesRules.item_value,
        isArray: true
      }
    }
  },
  {
    value: "data",
    label: "Data container",
    component: ({ setValue, errors, value }) => <DataValue onChange={setValue} errors={errors} value={value} />,
    viewValueComponent: ({ value }) => <DataViewValue value={value} />,
    validation: {
      item_value: {
        ...defaultSubentriesRules.item_value,
        validJSON: true
      }
    },
    tooltipText: "CFEngine data containers store structured data, in JSON format. The top level must be either an array or an object of key-value pairs."
  }
];

const CALCULATED_TYPES_MAP = {
  "String": "string",
  "Array": "list",
  "Object": "data",
};

const useStyles = makeStyles()((theme) => ({
  formRow: {
    display: 'flex',
    paddingRight: 100,
    alignItems: 'baseline',
    '&:not(:last-of-type)': {
      marginBottom: theme.spacing(2.5),
    },
    ' >div': {
      flexGrow: 1
    },
    '.string': {
      display: 'flex',
      columnGap: 10
    }
  },
  label: {
    width: 142,
    flexShrink: 0,
    marginRight: theme.spacing(3),
    color: theme.palette.text.primary,
    fontWeight: 500,
  },
  fieldContainer: {
    flex: 1,
    '&.string': {
      display: 'flex'
    },
    '& .MuiOutlinedInput-root input': {
      boxShadow: 'none',
    },
  },
  tooltip: {
    color: theme.palette.grey[500],
    fontSize: 20
  }
}));

interface VariableType {
  value: string;
  label: string;
  component: (props: { setValue: (value: any) => void; errors: Record<string, string>; value: any }) => React.ReactElement;
  viewValueComponent: (props: { value: any }) => React.ReactElement;
  validation: Record<string, any>;
  tooltipText?: string;
}


interface VariableFormProps {
  identifier: string;
  setCmdbItem: (item: CmdbItem) => void;
  item?: CmdbItem;
}

interface VariableFormRef {
  validate: () => Promise<boolean>;
}

const VariableForm = forwardRef<VariableFormRef, VariableFormProps>(({ identifier, setCmdbItem, item }, ref) => {
  const { classes } = useStyles();
  const [name, setName] = useState(getFirstEntryName(item));
  const [value, setValue] = useState(getFirstEntryValue(item));
  const [description, setDescription] = useState(item?.description || '');
  const [tags, setTags] = useState(item?.tags || []);
  const [type, setType] = useState(item?.meta?.variableType || '');

  // Use autocomplete hook with custom response parser for variables
  const { options: variableOptions, loading: loadingVariables } = useAutocompleteField({
    query: name,
    fetchThunk: getAutocompleteVariables,
    parseResponse: (payload) => {
      // The API returns an object with full_definition as keys and name as values
      // We want to show the keys (full definitions) as options
      if (typeof payload === 'object' && !Array.isArray(payload)) {
        return Object.keys(payload);
      }
      return Array.isArray(payload) ? payload : [];
    }
  });

  const selectedType = useMemo(() => {
    return VARIABLE_VALUES_TYPES.find(typeItem => typeItem.value === type);
  }, [type]);

  const customSubentryRules = useMemo(() => {
    return { ...defaultSubentriesRules, ...(selectedType?.validation || {}) };
  }, [selectedType]);

  const { errors, validate, setFieldError, setErrors } = useValidation({ customSubentryRules });

  useEffect(() => {
    if (type !== '' || value === null) return;
    setType(CALCULATED_TYPES_MAP[value?.constructor?.name] || '');
  }, [type, value]);

  // clear validation errors when variable type is changed as they have different validation set
  useEffect(() => {
    setFieldError('value', '');
  }, [selectedType, setFieldError]);

  const validateForm = async (): Promise<boolean> => {
    if (type === '') {
      setErrors({ type: 'Type is required.' });
      return false;
    }
    
    const formItem: CmdbItem = {
      type: CMDB_ITEM_TYPES.VARIABLE,
      name,
      description,
      tags,
      meta: { variableType: type },
      entries: [{
        item_name: name,
        item_value: value,
        item_type: CMDB_ITEM_TYPES.VARIABLE,
        entry_id: item?.id || 0,
        id: 0,
        hostkey: ''
      }]
    };

    return await validate({ item: formItem, identifier });
  };

  const ValueComponent = useMemo(() => {
    if (!selectedType) return (<></>);
    return selectedType.component({ setValue, errors, value });
  }, [selectedType, setValue, value, errors]);

  useImperativeHandle(ref, () => ({
    validate: validateForm,
  }), [validate, type, name, description, tags, value, item, identifier, setErrors]);

  useEffect(() => {
    let processedValue = value;
    if (selectedType && value && selectedType.value === 'data') {
      try {
        processedValue = JSON.parse(value);
      } catch(e) {}
    }

    setCmdbItem({
      type: CMDB_ITEM_TYPES.VARIABLE,
      name,
      description,
      tags,
      meta: { variableType: type },
      entries: [{
        item_name: name,
        item_value: processedValue,
        item_type: CMDB_ITEM_TYPES.VARIABLE
      }]
    });
  }, [name, description, tags, value, type, selectedType, setCmdbItem]);

  return (
    <>
      <Box className={classes.formRow}>
        <Typography variant="body1" className={classes.label}>
          Name:
        </Typography>
        <Box className={classes.fieldContainer}>
          <Autocomplete
            freeSolo
            options={variableOptions}
            value={name}
            loading={loadingVariables}
            onInputChange={(_, newValue) => setName(newValue)}
            renderInput={(params) => (
              <TextField
                {...params}
                fullWidth
                variant="outlined"
                size="small"
                placeholder="Variable name"
                error={!!errors.item_name}
                helperText={errors.item_name}
              />
            )}
          />
        </Box>
      </Box>
      <Box className={classes.formRow}>
        <Typography variant="body1" className={classes.label}>
          Value:
        </Typography>
        <Box className={`${classes.fieldContainer} ${type}`}>
          <Box>
            <Box className="flex flex--align_center">
              <Select
                displayEmpty
                value={type}
                onChange={(event) => setType(event.target.value)}
                input={<OutlinedInput />}
                IconComponent={ExpandMoreIcon}
                MenuProps={{
                  disableAutoFocusItem: true
                }}
                error={!!errors.type}
              >
                <MenuItem disabled value="">
                  Select a type
                </MenuItem>
                {VARIABLE_VALUES_TYPES.map((option) => (
                  <MenuItem key={option.value} value={option.value}>
                    {option.label}
                  </MenuItem>
                ))}
              </Select>
              {selectedType?.tooltipText && (
                <Tooltip
                  title={selectedType?.tooltipText}
                  arrow
                  placement="right"
                  sx={{ ml: 1 }}
                >
                  <HelpIcon className={classes.tooltip} />
                </Tooltip>
              )}
            </Box>
            <FormHelperText error>{errors.type}</FormHelperText>
          </Box>
          {ValueComponent}
        </Box>
      </Box>
      <Box className={classes.formRow}>
        <Typography variant="body1" className={classes.label}>
          Tags:
        </Typography>
        <Box className={classes.fieldContainer}>
          <Autocomplete
            multiple
            freeSolo
            options={[]}
            value={tags}
            onChange={(_, newValue) => setTags(newValue)}
            renderValue={(value, getItemProps) =>
              value.map((option, index) => {
                const { key, ...chipProps } = getItemProps({ index });
                return (
                  <Chip
                    key={key}
                    variant="outlined"
                    label={option}
                    {...chipProps}
                  />
                );
              })
            }
            renderInput={(params) => (
              <TextField
                {...params}
                variant="outlined"
                placeholder='Tags'
                error={!!errors.tags}
                helperText={errors.tags}
              />
            )}
          />
          <FormHelperText>Press enter to add new tags</FormHelperText>
        </Box>
      </Box>
      <Box className={classes.formRow}>
        <Typography variant="body1" className={classes.label}>
          Description:
        </Typography>
        <Box className={classes.fieldContainer}>
          <TextField
            variant="outlined"
            multiline
            fullWidth
            rows={3}
            placeholder="Variable description"
            value={description}
            onChange={(e) => setDescription(e.target.value)}
            error={!!errors.description}
            helperText={errors.description}
          />
        </Box>
      </Box>
    </>
  );
});

export default VariableForm;
