import React, { useEffect, useState, forwardRef, useImperativeHandle, useMemo } from 'react';
import {
  Box,
  Typography,
  TextField,
  MenuItem,
  Select,
  OutlinedInput,
  FormHelperText
} from '@mui/material';
import { ExpandMore as ExpandMoreIcon } from '@mui/icons-material';

import { makeStyles } from 'tss-react/mui';
import { useSelector } from 'react-redux';

import useValidation, { defaultRules } from '../../../hooks/useValidation';
import { PolicyEvaluationForm, PolicyEvaluationView } from './types/PolicyEvaluation';
import { selectAddedPolicyConfigurationsIds } from '../../../store/cmdbSlice/selectors';
import { CmdbItem, CmdbSubEntry } from '../../../store/cmdbSlice/thunks';
import { CMDB_ITEM_TYPES } from '../../../store/constants';
import { getPolicyId } from 'src/components/Cmdb/utils';


export const POLICY_TYPES = [
  {
    id: "autorun",
    label: "Enable autorun",
    component: () => <></>,
    validation: {
      item_value: {
        required: false
      }
    },
    description: "Automatically evaluate bundles tagged with autorun.",
    getSubEntries: (): CmdbSubEntry[] => [
      {
        item_name: 'default:services_autorun',
        item_value: null,
        item_type: CMDB_ITEM_TYPES.CLASS
      }
    ]
  },
  {
    id: "eval_policy_every_minute",
    label: "Evaluate policy every minute",
    description: "cf-execd will have a schedule of any, meaning it starts the agent every minute when it wakes up, and with a delay of 0 to 1 minute (splay time).",
    component: () => <></>,
    validation: {},
    getSubEntries: (): CmdbSubEntry[] => [
      {
        item_name: 'default:def.control_executor_splaytime',
        item_value: "1",
        item_type: CMDB_ITEM_TYPES.VARIABLE
      },
      {
        item_name: 'default:def.control_executor_schedule',
        item_value: ["any"],
        item_type: CMDB_ITEM_TYPES.VARIABLE
      }
    ]
  },
  {
    id: "enable_monitoring_for_non_hubs",
    label: "Enable monitoring data for non-hubs",
    description: "What monitoring data hosts should report to the hub. Note: For monitoring data to work, you must set this on both the hub and the host(s) you want data from. ",
    component: () => <></>,
    validation: {},
    getSubEntries: (): CmdbSubEntry[] => [
      {
        item_name: 'default:def.default_data_select_host_monitoring_include',
        item_value: [".*"],
        item_type: CMDB_ITEM_TYPES.VARIABLE
      }
    ]
  },
  {
    id: "eval_policy",
    label: "Evaluate policy",
    description: "This data controls which policy files will be read (added to inputs), and which bundles inside policy files to evaluate (added to bundlesequence).",
    component: ({ setValue, errors, value, classes }) => <PolicyEvaluationForm onChange={setValue} errors={errors} bundles={value?.bundles} files={value?.files} classes={classes} />,
    viewComponent: ({ onValueClick, value, classes }) => <PolicyEvaluationView bundles={value?.bundles} files={value?.files} classes={classes} onValueClick={onValueClick} />,
    validation: {},
    getSubEntries: (value): CmdbSubEntry[] => [
      {
        item_name: 'default:def.augment_inputs',
        item_value: (value?.files ? value.files.split("\n") : []),
        item_type: 'variable',
        error_field: 'files'
      },
      {
        item_name: 'default:def.control_common_bundlesequence_end',
        item_value: (value?.bundles ? value.bundles.split("\n") : []),
        item_type: 'variable',
        error_field: 'bundles'
      },
    ],
    getValueFromSubEntries: (subentries) => {
      return {
        files: ((subentries.find(item => item.item_name === 'default:def.augment_inputs'))?.item_value ?? []).join("\n"),
        bundles: ((subentries.find(item => item.item_name === 'default:def.control_common_bundlesequence_end'))?.item_value ?? []).join("\n")
      }
    }
  }
];

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,
  },
}));

const defaultSubentriesRules = {
  item_name: {
    required: true,
    checkExists: true
  },
  item_value: {
    required: true
  }
};

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

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

interface PolicyEvaluationValue {
  files?: string;
  bundles?: string;
}

const PolicyConfigurationForm = forwardRef<PolicyConfigurationFormRef, PolicyConfigurationFormProps>(({ identifier, setCmdbItem, item }, ref) => {
  const { classes } = useStyles();
  const [name, setName] = useState(item?.name || '');
  const [value, setValue] = useState<PolicyEvaluationValue | null>(item?.value || null);
  const [description, setDescription] = useState(item?.description || '');
  const tags = [];
  const [policyId, setPolicyId] = useState(getPolicyId(item));

  const addedPolicyConfigurationsIds = useSelector(selectAddedPolicyConfigurationsIds);

  const selectedType = useMemo(() => {
    return POLICY_TYPES.find(item => item.id === policyId)
  }, [policyId]);

  const customRules = item !== undefined
    ? { ...defaultRules, name: { ...defaultRules.name, checkExists: false }, ...(selectedType?.validation || {}) }
    : { ...defaultRules, ...(selectedType?.validation || {}) };


  const { errors, validate, clearErrors } = useValidation({
    customRules,
    customSubentryRules: { ...defaultSubentriesRules, ...(selectedType?.validation ?? {}) }
  });

  // clear validation errors when policy config is changed and set values from config
  useEffect(() => {
    if (!selectedType) return;
    clearErrors();
    setName(selectedType?.label);
    setDescription(item?.description ?? selectedType?.description);
    if (selectedType.hasOwnProperty('getValueFromSubEntries')) {
      setValue(selectedType.getValueFromSubEntries(item?.entries || []));
    }
  }, [selectedType, item]);


  const validateForm = async (): Promise<boolean> => {
    return await validate({
      item: {
        type: CMDB_ITEM_TYPES.POLICY_CONFIGURATION,
        name,
        description,
        tags,
        meta: { policyId },
        entries: selectedType.getSubEntries(value).map(subentry => ({ ...subentry, entry_id: item?.id }))
      },
      identifier
    });
  };

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

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

  useEffect(() => {
    if (!selectedType) return;
    setCmdbItem({
      type: CMDB_ITEM_TYPES.POLICY_CONFIGURATION,
      name,
      description,
      tags,
      meta: { policyId },
      entries: selectedType.getSubEntries(value)
    });
  }, [name, description, tags, policyId, value, selectedType, setCmdbItem]);

  return (
    <>
      <Box className={classes.formRow}>
        <Typography variant="body1" className={classes.label}>
          Configuration:
        </Typography>
        <Box className={policyId}>
          <Select
            displayEmpty
            value={policyId}
            onChange={(event) => setPolicyId(event.target.value)}
            input={<OutlinedInput />}
            IconComponent={ExpandMoreIcon}
            MenuProps={{
              autoWidth: false
            }}
          >
            <MenuItem disabled value="">
              Select a type
            </MenuItem>
            {POLICY_TYPES.map((option) => (
              <MenuItem key={option.id} value={option.id} disabled={!!addedPolicyConfigurationsIds.find(item => item === option.id)}>
                {option.label}
              </MenuItem>
            ))}
          </Select>
        </Box>
      </Box>
      {ValueComponent}
      {selectedType && <Box className={classes.formRow}>
        <Typography variant="body1" className={classes.label}>
          Description:
        </Typography>
        <Box className={classes.fieldContainer}>
          <TextField
            variant="outlined"
            multiline
            fullWidth
            rows={3}
            placeholder="Policy configuration description"
            value={description}
            onChange={(e) => setDescription(e.target.value)}
            error={!!errors.description}
            helperText={errors.description}
          />
        </Box>
      </Box>
      }
      {errors.item_name && <FormHelperText error>{errors.item_name}</FormHelperText>}
    </>
  );
});

export default PolicyConfigurationForm;
