import { mdiHarddiskPlus } from "@mdi/js";
import Icon from "@mdi/react";
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import DeleteIcon from "@mui/icons-material/Delete";
import ExpandIcon from "@mui/icons-material/Expand";
import { Button, IconButton, Paper, Tooltip, Typography } from "@mui/material";
import React from "react";
import { DiskImage } from "../../api/DiskImageApi";
import { ServerApi } from "../../api/ServerApi";
import { VMFileDisk, VMInfo, VMState } from "../../api/VMApi";
import { ConvertDiskImageDialog } from "../../dialogs/ConvertDiskImageDialog";
import { useConfirm } from "../../hooks/providers/ConfirmDialogProvider";
import { VMDiskFileWidget } from "../vms/VMDiskFileWidget";
import { CheckboxInput } from "./CheckboxInput";
import { DiskBusSelect } from "./DiskBusSelect";
import { DiskImageSelect } from "./DiskImageSelect";
import { DiskSizeInput } from "./DiskSizeInput";
import { SelectInput } from "./SelectInput";
import { TextInput } from "./TextInput";

export function VMDisksList(p: {
  vm: VMInfo;
  state?: VMState;
  onChange?: () => void;
  editable: boolean;
  diskImagesList: DiskImage[];
}): React.ReactElement {
  const [currBackupRequest, setCurrBackupRequest] = React.useState<
    VMFileDisk | undefined
  >();

  const addNewDisk = () => {
    p.vm.file_disks.push({
      format: "QCow2",
      size: 10000 * 1000 * 1000,
      bus: "Virtio",
      delete: false,
      name: `disk${p.vm.file_disks.length}`,
      new: true,
    });
    p.onChange?.();
  };

  const handleBackupRequest = (disk: VMFileDisk) => {
    setCurrBackupRequest(disk);
  };

  const handleFinishBackup = () => {
    setCurrBackupRequest(undefined);
  };

  return (
    <>
      {/* disks list */}
      {p.vm.file_disks.map((d, num) => (
        <DiskInfo
          // eslint-disable-next-line react-x/no-array-index-key
          key={num}
          editable={p.editable}
          canBackup={!p.editable && !d.new && p.state !== "Running"}
          disk={d}
          onChange={p.onChange}
          removeFromList={() => {
            p.vm.file_disks.splice(num, 1);
            p.onChange?.();
          }}
          onRequestBackup={handleBackupRequest}
          diskImagesList={p.diskImagesList}
        />
      ))}

      {p.vm.file_disks.length === 0 && (
        <Typography style={{ textAlign: "center", paddingTop: "25px" }}>
          No disk file yet!
        </Typography>
      )}

      {p.editable && <Button onClick={addNewDisk}>Add new disk</Button>}

      {/* Disk backup */}
      {currBackupRequest && (
        <ConvertDiskImageDialog
          backup
          onCancel={handleFinishBackup}
          onFinished={handleFinishBackup}
          vm={p.vm}
          disk={currBackupRequest}
        />
      )}
    </>
  );
}

function DiskInfo(p: {
  editable: boolean;
  canBackup: boolean;
  disk: VMFileDisk;
  onChange?: () => void;
  removeFromList: () => void;
  onRequestBackup: (disk: VMFileDisk) => void;
  diskImagesList: DiskImage[];
}): React.ReactElement {
  const confirm = useConfirm();

  const expandDisk = () => {
    if (p.disk.resize === true) {
      p.disk.resize = false;
      p.disk.size = p.disk.originalSize!;
    } else {
      p.disk.resize = true;
      p.disk.originalSize = p.disk.size!;
    }

    p.onChange?.();
  };

  const deleteDisk = async () => {
    if (p.disk.deleteType) {
      p.disk.deleteType = undefined;
      p.onChange?.();
      return;
    }

    const keepFile = await confirm(
      `You asked to delete the disk ${p.disk.name}. Do you want to keep the block file or not ? `,
      "Delete disk",
      "Keep the file",
      "Delete the file"
    );

    if (!(await confirm("Do you really want to delete this disk?"))) return;

    p.disk.deleteType = keepFile ? "keepfile" : "deletefile";
    p.onChange?.();
  };

  if (!p.editable || !p.disk.new)
    return (
      <>
        <VMDiskFileWidget
          {...p}
          secondaryAction={
            <>
              {p.editable && !p.disk.deleteType && (
                <IconButton
                  edge="end"
                  aria-label="expand disk"
                  onClick={expandDisk}
                >
                  {p.disk.resize === true ? (
                    <Tooltip title="Cancel disk expansion">
                      <ExpandIcon color="error" />
                    </Tooltip>
                  ) : (
                    <Tooltip title="Increase disk size">
                      <ExpandIcon />
                    </Tooltip>
                  )}
                </IconButton>
              )}

              {p.editable && (
                <IconButton
                  edge="end"
                  aria-label="delete disk"
                  onClick={deleteDisk}
                >
                  {p.disk.deleteType ? (
                    <Tooltip title="Cancel disk removal">
                      <CheckCircleIcon />
                    </Tooltip>
                  ) : (
                    <Tooltip title="Remove disk">
                      <DeleteIcon />
                    </Tooltip>
                  )}
                </IconButton>
              )}

              {p.canBackup && (
                <Tooltip title="Backup this disk">
                  <IconButton
                    onClick={() => {
                      p.onRequestBackup(p.disk);
                    }}
                  >
                    <Icon path={mdiHarddiskPlus} size={1} />
                  </IconButton>
                </Tooltip>
              )}
            </>
          }
        />

        {/* New disk size*/}
        {p.disk.resize && !p.disk.deleteType && (
          <DiskSizeInput
            editable
            label="New disk size (GB)"
            value={p.disk.size}
            onChange={(v) => {
              p.disk.size = v;
              p.onChange?.();
            }}
          />
        )}
      </>
    );

  return (
    <Paper elevation={3} style={{ margin: "10px", padding: "10px" }}>
      <div style={{ display: "flex", justifyContent: "space-between" }}>
        <TextInput
          editable={true}
          label="Disk name"
          size={ServerApi.Config.constraints.disk_name_size}
          checkValue={(v) => /^[a-zA-Z0-9]+$/.test(v)}
          value={p.disk.name}
          onValueChange={(v) => {
            p.disk.name = v ?? "";
            p.onChange?.();
          }}
        />
        <IconButton onClick={p.removeFromList}>
          <DeleteIcon />
        </IconButton>
      </div>

      <SelectInput
        editable={true}
        label="Disk format"
        options={[
          { label: "Raw file", value: "Raw" },
          { label: "QCow2", value: "QCow2" },
        ]}
        value={p.disk.format}
        onValueChange={(v) => {
          p.disk.format = v as any;

          if (p.disk.format === "Raw") p.disk.is_sparse = true;

          p.onChange?.();
        }}
      />

      {/* Bus selection */}
      <DiskBusSelect
        editable
        value={p.disk.bus}
        onValueChange={(v) => {
          p.disk.bus = v;
          p.onChange?.();
        }}
      />

      {/* Raw disk: choose sparse mode */}
      {p.disk.format === "Raw" && (
        <CheckboxInput
          editable
          label="Sparse file"
          checked={p.disk.is_sparse}
          onValueChange={(v) => {
            if (p.disk.format === "Raw") p.disk.is_sparse = v;
            p.onChange?.();
          }}
        />
      )}

      {/* Resize disk image */}
      {!!p.disk.from_image && (
        <CheckboxInput
          editable
          checked={p.disk.resize}
          label="Resize disk file"
          onValueChange={(v) => {
            p.disk.resize = v;
            p.onChange?.();
          }}
        />
      )}

      {/* Disk size */}
      {(!p.disk.from_image || p.disk.resize === true) && (
        <DiskSizeInput
          editable
          value={p.disk.size}
          onChange={(v) => {
            p.disk.size = v;
            p.onChange?.();
          }}
        />
      )}

      {/* Disk image selection */}
      <DiskImageSelect
        label="Use disk image as template"
        list={p.diskImagesList}
        value={p.disk.from_image}
        onValueChange={(v) => {
          p.disk.from_image = v;
          p.onChange?.();
        }}
      />
    </Paper>
  );
}