import { useEffect, useState } from 'react';
import type { KeyboardEvent } from 'react';
import { useSelector } from 'react-redux';
import {
  Box,
  Button,
  Checkbox,
  Chip,
  FormControl,
  FormControlLabel,
  ListItemText,
  MenuItem,
  Select
} from '@mui/material';
import AddIcon from '@mui/icons-material/Add';
import CloseIcon from '@mui/icons-material/Close';

import Dialog from '../Dialog';
import { ClassFinderDialog } from './ClassFinderDialog';
import {
  fetchTreeMetadata,
  selectTreeMetadata,
  selectTreeMetadataLoaded
} from './store';
import { useHostTreeDispatch } from './store/dispatchType';
import { NAME_PATTERN } from './constants';

const isValidClassExpression = (expr: string): boolean => {
  try {
    // A CFEngine class expression (e.g. `linux.!windows|(ubuntu_22)`) is a
    // subset of regular-expression syntax, so constructing a RegExp catches
    // structurally malformed input such as unbalanced parentheses. This is a
    // loose syntactic check, not a full class-expression validation.
    new RegExp(expr);
    return true;
  } catch {
    return false;
  }
};

interface Subcategory {
  label: string;
  classRegex: string;
}

interface SubmitPayload {
  name: string;
  subcategories: Subcategory[];
  sharedPermission: string[];
  global: boolean;
}

interface Props {
  open: boolean;
  existingNames: string[];
  onClose: () => void;
  onSubmit: (payload: SubmitPayload) => Promise<void> | void;
}

export const NewProfileDialog = ({ open, existingNames, onClose, onSubmit }: Props) => {
  const dispatch = useHostTreeDispatch();
  const treeMetadata = useSelector(selectTreeMetadata);
  const treeMetadataLoaded = useSelector(selectTreeMetadataLoaded);
  const roles = treeMetadata?.roles ?? [];
  const isAdmin = treeMetadata?.isAdmin ?? false;

  // ---- form state ----
  const [name, setName] = useState('');
  const [nameError, setNameError] = useState('');

  const [selectedRoles, setSelectedRoles] = useState<string[]>([]);
  const [global, setGlobal] = useState(false);

  const [subcategories, setSubcategories] = useState<Subcategory[]>([]);
  const [nodeLabel, setNodeLabel] = useState('');
  const [nodeClassExpr, setNodeClassExpr] = useState('');
  const [nodeLabelError, setNodeLabelError] = useState('');
  const [nodeClassError, setNodeClassError] = useState('');

  const [submitting, setSubmitting] = useState(false);
  const [classFinderOpen, setClassFinderOpen] = useState(false);

  const handlePickClass = (cls: string) => {
    // Match the legacy behaviour: append the chosen class to the existing
    // value with `|` (regex alternation) separator. Trim whitespace first.
    setNodeClassExpr(prev => {
      const trimmed = prev.trim();
      if (!trimmed) return cls;
      return `${trimmed}|${cls}`;
    });
  };

  // Reset state every time the dialog opens
  useEffect(() => {
    if (open) {
      setName('');
      setNameError('');
      setSelectedRoles([]);
      setGlobal(false);
      setSubcategories([]);
      setNodeLabel('');
      setNodeClassExpr('');
      setNodeLabelError('');
      setNodeClassError('');
      setSubmitting(false);
    }
  }, [open]);

  // Load roles + admin flag once on first open. The slice caches the result
  // for the lifetime of the page so re-opens are instant.
  useEffect(() => {
    if (!open || treeMetadataLoaded) return;
    dispatch(fetchTreeMetadata());
  }, [dispatch, open, treeMetadataLoaded]);

  // ---- validation helpers (mirror legacy nodeAddValidation + treeTitleValidation) ----
  const validateName = (): boolean => {
    const trimmed = name.trim();
    if (trimmed === '') {
      setNameError('Name cannot be empty');
      return false;
    }
    if (!NAME_PATTERN.test(trimmed)) {
      setNameError(
        'Name can only contain alphanumeric characters, spaces, "_" or "-" (max 100 characters)'
      );
      return false;
    }
    if (existingNames.some(n => n.toLowerCase() === trimmed.toLowerCase())) {
      setNameError('Tree already exists, please choose a different name');
      return false;
    }
    setNameError('');
    return true;
  };

  const validateNodeInputs = (): Subcategory | null => {
    setNodeLabelError('');
    setNodeClassError('');
    let ok = true;
    if (nodeLabel.trim() === '') {
      setNodeLabelError('Label cannot be empty');
      ok = false;
    } else if (!NAME_PATTERN.test(nodeLabel)) {
      setNodeLabelError(
        'Label can only contain alphanumeric characters, spaces, "_" or "-" (max 100 characters)'
      );
      ok = false;
    }
    if (nodeClassExpr.trim() === '') {
      setNodeClassError('Class expression cannot be empty');
      ok = false;
    } else if (!isValidClassExpression(nodeClassExpr)) {
      setNodeClassError('Invalid class expression');
      ok = false;
    }
    return ok ? { label: nodeLabel.trim(), classRegex: nodeClassExpr.trim() } : null;
  };

  const handleAddSubcategory = () => {
    const sub = validateNodeInputs();
    if (!sub) return;
    setSubcategories(prev => [...prev, sub]);
    setNodeLabel('');
    setNodeClassExpr('');
    setNodeLabelError('');
    setNodeClassError('');
  };

  const handleRemoveSubcategory = (index: number) => {
    setSubcategories(prev => prev.filter((_, i) => i !== index));
  };

  // Enter inside a subcategory input adds the subcategory rather than doing
  // nothing, so users get clear feedback instead of assuming it saved.
  const handleSubcategoryKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
    if (e.key === 'Enter') {
      e.preventDefault();
      handleAddSubcategory();
    }
  };

  const handleSubmit = async () => {
    if (!validateName()) return;

    let finalSubs = [...subcategories];
    // If the current subcategory inputs have anything typed in, validate and add
    if (nodeLabel.trim() || nodeClassExpr.trim()) {
      const sub = validateNodeInputs();
      if (!sub) return;
      finalSubs = [...finalSubs, sub];
    }

    setSubmitting(true);
    try {
      await onSubmit({
        name: name.trim(),
        subcategories: finalSubs,
        sharedPermission: selectedRoles,
        global
      });
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <Dialog
      open={open}
      onClose={onClose}
      title="Categorize your host"
      size="sm"
      fullWidth
      actionButtons={
        <>
          <Button onClick={onClose} variant="outlined" color="inherit">
            Cancel
          </Button>
          <Button
            className="btn-primary"
            onClick={handleSubmit}
            variant="contained"
            color="primary"
            disabled={submitting}
          >
            Ok
          </Button>
        </>
      }
    >
      <form
        className="form-horizontal"
        onKeyDown={e => {
          // Suppress implicit form submission on Enter; the subcategory inputs
          // handle Enter explicitly (see handleSubcategoryKeyDown) and the
          // profile is saved via the "Ok" button.
          if (e.key === 'Enter') e.preventDefault();
        }}
      >
        {/* ---- Title ---- */}
        <div className="control-group">
          <label htmlFor="astrolabe-add-tree-label" className="control-label">
            Title
          </label>
          <div className="controls">
            <input
              id="astrolabe-add-tree-label"
              type="text"
              value={name}
              autoFocus
              onChange={e => setName(e.target.value)}
            />
            {nameError && (
              <p className="astrolabeAddNodeDialogError" style={{ color: '#b94a48' }}>
                {nameError}
              </p>
            )}
          </div>
        </div>

        {/* ---- Roles multi-select ---- */}
        <div className="control-group">
          <label className="control-label">Share categorization with these user roles</label>
          <div className="controls">
            <button
              type="button"
              className="multiselect dropdown-toggle"
              aria-hidden
              tabIndex={-1}
              style={{ position: 'absolute', width: 1, height: 1, padding: 0, opacity: 0, pointerEvents: 'none' }}
              onClick={e => e.preventDefault()}
            />
            <span style={{ position: 'absolute', overflow: 'hidden', width: 1, height: 1 }}>
              {roles.map(r => (
                <input
                  key={r}
                  type="checkbox"
                  value={r}
                  checked={selectedRoles.indexOf(r) > -1}
                  onChange={e => {
                    setSelectedRoles(prev =>
                      e.target.checked ? [...prev, r] : prev.filter(x => x !== r)
                    );
                  }}
                />
              ))}
            </span>

            <FormControl size="small" sx={{ minWidth: 240 }}>
              <Select
                multiple
                value={selectedRoles}
                onChange={e => {
                  const value = e.target.value;
                  setSelectedRoles(typeof value === 'string' ? value.split(',') : value);
                }}
                displayEmpty
                renderValue={selected =>
                  selected.length === 0 ? (
                    <em style={{ color: '#999' }}>None selected</em>
                  ) : (
                    <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
                      {(selected as string[]).map(r => (
                        <Chip key={r} label={r} size="small" />
                      ))}
                    </Box>
                  )
                }
              >
                {roles.length === 0 ? (
                  <MenuItem disabled>(no roles available)</MenuItem>
                ) : (
                  roles.map(r => (
                    <MenuItem key={r} value={r}>
                      <Checkbox checked={selectedRoles.indexOf(r) > -1} />
                      <ListItemText primary={r} />
                    </MenuItem>
                  ))
                )}
              </Select>
            </FormControl>
          </div>
        </div>

        {/* ---- Global checkbox (admin-only) ---- */}
        {isAdmin && (
          <div className="control-group">
            <div className="controls">
              <FormControlLabel
                control={
                  <Checkbox checked={global} onChange={e => setGlobal(e.target.checked)} />
                }
                label="Add this categorization to all users' accounts"
              />
            </div>
          </div>
        )}

        {/* ---- Subcategories ---- */}
        <fieldset style={{ marginTop: 16, paddingTop: 12, borderTop: '1px solid #ddd' }}>
          <legend style={{ fontSize: 16, fontWeight: 500, paddingBottom: 8 }}>
            Add subcategories
          </legend>

          {subcategories.length > 0 && (
            <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1, mb: 2 }}>
              {subcategories.map((sub, i) => (
                <Chip
                  key={`${sub.label}-${i}`}
                  label={`${sub.label} (${sub.classRegex})`}
                  onDelete={() => handleRemoveSubcategory(i)}
                  deleteIcon={<CloseIcon />}
                  size="small"
                />
              ))}
            </Box>
          )}

          <div className="control-group">
            <label htmlFor="hosts-tree-new-sub-label" className="control-label">
              Title
            </label>
            <div className="controls">
              <input
                id="hosts-tree-new-sub-label"
                type="text"
                value={nodeLabel}
                onChange={e => setNodeLabel(e.target.value)}
                onKeyDown={handleSubcategoryKeyDown}
              />
              {nodeLabelError && (
                <p className="astrolabeAddNodeDialogError" style={{ color: '#b94a48' }}>
                  {nodeLabelError}
                </p>
              )}
            </div>
          </div>

          <div className="control-group">
            <label htmlFor="hosts-tree-new-sub-class" className="control-label">
              CFEngine class expression (.*+[])
            </label>
            <div className="controls">
              <input
                id="hosts-tree-new-sub-class"
                type="text"
                value={nodeClassExpr}
                onChange={e => setNodeClassExpr(e.target.value)}
                onKeyDown={handleSubcategoryKeyDown}
              />
              <div className="classFinderIcon" style={{ marginTop: 4 }}>
                <a
                  href="#"
                  onClick={e => {
                    e.preventDefault();
                    setClassFinderOpen(true);
                  }}
                >
                  Show all classes
                </a>
              </div>
              {nodeClassError && (
                <p className="astrolabeAddNodeDialogError" style={{ color: '#b94a48' }}>
                  {nodeClassError}
                </p>
              )}
            </div>
          </div>

          <div className="control-group">
            <div className="controls">
              <Button
                size="small"
                variant="outlined"
                startIcon={<AddIcon />}
                onClick={handleAddSubcategory}
              >
                Add another subcategory
              </Button>
            </div>
          </div>
        </fieldset>
      </form>

      <ClassFinderDialog
        open={classFinderOpen}
        onClose={() => setClassFinderOpen(false)}
        onSelect={handlePickClass}
      />
    </Dialog>
  );
};
