import { K8s } from '@kinvolk/headlamp-plugin/lib';
import { Loader } from '@kinvolk/headlamp-plugin/lib/CommonComponents';
import { Alert, Card } from '@mui/material';
import { Typography } from '@mui/material';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogTitle from '@mui/material/DialogTitle';
import FormControl from '@mui/material/FormControl';
import Grid from '@mui/material/Grid';
import Link from '@mui/material/Link';
import TextField from '@mui/material/TextField';
import React from 'react';
import { useHistory } from 'react-router-dom';
import DriverSelect from './DriverSelect';
import { DriverInfo } from './useInfo';

export function generateClusterName(existingNames: string[]): string {
  const baseName = 'minikube';
  let newName = baseName;
  let counter = 1;

  while (existingNames.includes(newName)) {
    newName = `${baseName}-${counter}`;
    counter++;
  }

  return newName;
}

/** Validates that a minikube profile name is well-formed. Returns an error string, or null if valid. */
export function isValidClusterName(name: string): string | null {
  if (!name) return 'Cluster name is required';
  if (name.length > 63) return 'Cluster name must be 63 characters or fewer';
  if (!/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(name)) {
    return 'Cluster name must start and end with a letter or number and contain only letters, numbers, and hyphens';
  }
  return null;
}

export interface CommandDialogProps {
  /** Is the dialog open? */
  open: boolean;
  /** Function to call when the dialog is closed */
  onClose: (cancel?: boolean) => void;
  /** Function to call when the user confirms the action */
  onConfirm: (data: { clusterName: string; driver: string }) => void;
  /** Command to run, like stop, start, delete... */
  command: string;
  /** The title of the form */
  title?: string;
  /** Is the command about to run? */
  acting: boolean;
  /** Command is actually running. There is some time before running where it can still be cancelled. */
  running: boolean;
  /** Output lines coming from the command. */
  actingLines?: string[];
  /** Is the command done? */
  commandDone: boolean;
  /** Did the command fail (non-zero exit)? */
  commandError?: boolean;
  /** should it use a dialog or use a grid? */
  useGrid?: boolean;
  /** The cluster context to act on */
  initialClusterName?: string;
  /** Ask for the cluster name. Otherwise the initialClusterName is used. */
  askClusterName?: boolean;
  info: DriverInfo | null;
  /** Is minikube installed and available? null = checking, true = yes, false = no */
  minikubeAvailable?: boolean | null;
}

/**
 * A form to confirm a command on a cluster.
 */
export default function CommandDialog({
  open,
  onClose,
  onConfirm,
  command,
  title,
  acting,
  running,
  actingLines,
  commandDone,
  commandError,
  useGrid,
  initialClusterName,
  askClusterName,
  info,
  minikubeAvailable,
}: CommandDialogProps) {
  const [clusterName, setClusterName] = React.useState(initialClusterName);
  const [driver, setDriver] = React.useState('');
  const [nameError, setNameError] = React.useState<string | null>(null);

  const outputRef = React.useRef<HTMLDivElement>(null);

  const history = useHistory();
  const clusters = K8s.useClustersConf();
  const clusterNames = React.useMemo(() => Object.keys(clusters || {}), [clusters]);

  React.useEffect(() => {
    if (open && !initialClusterName && askClusterName) {
      setClusterName(generateClusterName(clusterNames));
    }
    // Only generate a new name when dialog is opened, not on every clusterNames change
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open, initialClusterName, askClusterName]);

  React.useEffect(
    function scrollOutputToBottom() {
      if (outputRef.current) {
        outputRef.current.scrollTop = outputRef.current.scrollHeight;
      }
    },
    [actingLines]
  );

  if (acting && open && !running) {
    if (askClusterName) {
      return <Loader title={`Loading data for ${title}`} />;
    } else {
      return null;
    }
  }

  const content = (
    <>
      {minikubeAvailable === null && !acting && (
        <Loader title="Checking minikube availability..." />
      )}
      {minikubeAvailable === false && (
        <Box sx={{ mt: 1, p: 2, bgcolor: 'warning.light', borderRadius: 1 }}>
          <Typography color="warning.contrastText" gutterBottom>
            minikube was not found on your system.
          </Typography>
          <Typography variant="body2" color="warning.contrastText">
            Install it from{' '}
            <Link
              href="https://minikube.sigs.k8s.io/docs/start/"
              target="_blank"
              rel="noopener noreferrer"
            >
              minikube.sigs.k8s.io/docs/start
            </Link>{' '}
            and make sure the <code>minikube</code> command is in your PATH.
          </Typography>
        </Box>
      )}
      {!askClusterName && !acting && (
        <Typography>
          {`Are you sure you want to "${command}" the cluster "${clusterName}"?`}
        </Typography>
      )}

      {askClusterName && !acting && (
        <>
          <FormControl fullWidth>
            <Box pt={2}>
              <TextField
                id="cluster-name-input"
                label="Cluster Name"
                value={clusterName}
                onChange={function handleNameChange(event: React.ChangeEvent<HTMLInputElement>) {
                  const name = event.target.value;
                  setClusterName(name);
                  if (clusterNames.includes(name)) {
                    setNameError('Cluster name is already taken');
                  } else {
                    setNameError(isValidClusterName(name));
                  }
                }}
                variant="outlined"
                error={!!nameError}
                helperText={nameError || ''}
              />
            </Box>
          </FormControl>
          <DriverSelect driver={driver} setDriver={setDriver} info={info} />
          {info && info.hyperVEnabled === false && (
            <Alert severity="warning">
              {`Warning: HyperV is not enabled. You can either enable it or use another driver.`}
            </Alert>
          )}
          {info && parseFloat(info.freeRam) < 2 && (
            <Alert severity="warning">
              {`Warning: You have less than 2GB of free Memory available. This may affect performance.`}
            </Alert>
          )}
          {info && parseFloat(info.ram) <= 8 && (
            <Alert severity="warning">
              {`Warning: We recommend more than 8GB of Memory Total. This may affect performance.`}
            </Alert>
          )}
          {info && parseFloat(info.diskFree) < 22 && (
            <Alert severity="warning">
              {`Warning: You have less than 22GB of free Disk available. This may affect performance.`}
            </Alert>
          )}
        </>
      )}
      {acting && actingLines && Array.isArray(actingLines) && actingLines.length > 0 && (
        <Card
          ref={outputRef}
          variant="outlined"
          sx={{
            mt: 2,
            p: 2,
            maxHeight: 300,
            overflowY: 'auto',
            fontFamily: 'monospace',
            fontSize: '0.85rem',
          }}
        >
          {actingLines.map((line, index) => (
            <Typography
              key={index}
              variant="body2"
              sx={{
                fontFamily: 'inherit',
                fontSize: 'inherit',
                whiteSpace: 'pre-wrap',
                wordBreak: 'break-all',
              }}
            >
              {line}
            </Typography>
          ))}
        </Card>
      )}
      {commandDone && commandError && (
        <Box sx={{ mt: 2, p: 2, bgcolor: 'error.light', borderRadius: 1 }}>
          <Typography color="error.contrastText">
            Command failed. Check the output above for details.
          </Typography>
        </Box>
      )}
      {commandDone && !commandError && (
        <Box sx={{ mt: 2, p: 2, bgcolor: 'success.light', borderRadius: 1 }}>
          <Typography color="success.contrastText">Command completed successfully.</Typography>
        </Box>
      )}
      {running && !commandDone && <Loader title={`Loading data for ${title}`} />}
    </>
  );

  const waitForDriver = command === 'start' && askClusterName ? driver === null : false;

  const buttons = (
    <>
      {!acting && waitForDriver && !info && <Loader title={`Detecting drivers...`} />}
      {!info && !waitForDriver && <Loader title={`Loading cluster info...`} />}
      {!acting && !waitForDriver && info && (
        <>
          {!useGrid && <Button onClick={() => onClose(true)}>Cancel</Button>}
          <Button
            onClick={() => {
              if (clusterName) {
                onConfirm({ clusterName, driver });
              }
            }}
            variant="contained"
            color="primary"
            disabled={(!!nameError && askClusterName) || minikubeAvailable === false}
          >
            {`${command}`}
          </Button>
        </>
      )}
      {!useGrid && commandDone && (
        <>
          <Button variant="contained" color="primary" onClick={() => onClose(false)}>
            Close
          </Button>
        </>
      )}
      {useGrid && commandDone && (
        // @todo:
        // Going to the Home doesn't work. Because of a bug in the way clusters are only
        // refreshed on the home page. So we can't navigate to the cluster page, as it is a 404.
        // Currently non cluster pages do not refresh the backend list of clusters.
        // That is why this link to the cluster does not work and is commented out.
        // https://github.com/headlamp-k8s/headlamp/issues/3040#issuecomment-2758929070
        // <>
        //   <Button onClick={() => {
        //     onClose();
        //     history.push(`/clusters/${clusterName}`);
        //   }}>View Cluster</Button>
        // </>
        <>
          <Button
            variant="contained"
            color="primary"
            onClick={() => {
              onClose(false);
              history.push(`/`);
            }}
          >
            Home
          </Button>
        </>
      )}
    </>
  );

  return useGrid ? (
    <Grid container spacing={2}>
      <Grid item xs={12}>
        <Typography>{title}</Typography>
      </Grid>
      <Grid item xs={12}>
        {content}
      </Grid>
      <Grid item xs={6}>
        {buttons}
      </Grid>
    </Grid>
  ) : (
    <Dialog open={open} onClose={() => onClose(false)}>
      <DialogTitle>{title}</DialogTitle>
      <DialogContent>{content}</DialogContent>
      <DialogActions>{buttons}</DialogActions>
    </Dialog>
  );
}
