/* eslint-disable @typescript-eslint/no-base-to-string */

import Editor from "@monaco-editor/react";
import BookIcon from "@mui/icons-material/Book";
import RefreshIcon from "@mui/icons-material/Refresh";
import { Grid, IconButton, InputAdornment, Tooltip } from "@mui/material";
import React from "react";
import { v4 as uuidv4 } from "uuid";
import YAML from "yaml";
import { VMInfo } from "../../api/VMApi";
import { RouterLink } from "../RouterLink";
import { CheckboxInput } from "./CheckboxInput";
import { EditSection } from "./EditSection";
import { SelectInput } from "./SelectInput";
import { TextInput } from "./TextInput";

interface CloudInitProps {
  vm: VMInfo;
  onChange?: () => void;
  editable: boolean;
}

export function CloudInitEditor(p: CloudInitProps): React.ReactElement {
  return (
    <>
      <EditSection>
        {/* Attach cloud init disk */}
        <CheckboxInput
          {...p}
          label="Attach Cloud Init disk"
          checked={p.vm.cloud_init.attach_config}
          onValueChange={(v) => {
            p.vm.cloud_init.attach_config = v;
            p.onChange?.();
          }}
        />
      </EditSection>
      <Grid container spacing={2}>
        <CloudInitMetadata
          {...p}
          editable={p.editable && p.vm.cloud_init.attach_config}
        />
        <CloudInitRawUserData
          {...p}
          editable={p.editable && p.vm.cloud_init.attach_config}
        />
        <CloudInitNetworkConfig
          {...p}
          editable={p.editable && p.vm.cloud_init.attach_config}
        />
        <CloudInitUserDataAssistant
          {...p}
          editable={p.editable && p.vm.cloud_init.attach_config}
        />
      </Grid>
    </>
  );
}

function CloudInitMetadata(p: CloudInitProps): React.ReactElement {
  // Regenerate instance id
  const reGenerateInstanceId = () => {
    p.vm.cloud_init.instance_id = uuidv4();
    p.onChange?.();
  };

  return (
    <EditSection title="Metadata">
      {/* Instance ID */}
      <TextInput
        {...p}
        label="Instance ID"
        value={p.vm.cloud_init.instance_id}
        onValueChange={(v) => {
          p.vm.cloud_init.instance_id = v;
          p.onChange?.();
        }}
        endAdornment={
          p.editable ? (
            <InputAdornment position="end">
              <Tooltip title="Generate a new instance ID">
                <IconButton onClick={reGenerateInstanceId}>
                  <RefreshIcon />
                </IconButton>
              </Tooltip>
            </InputAdornment>
          ) : (
            <></>
          )
        }
      />

      {/* Instance hostname */}
      <TextInput
        {...p}
        label="Local hostname"
        value={p.vm.cloud_init.local_hostname}
        onValueChange={(v) => {
          p.vm.cloud_init.local_hostname = v;
          p.onChange?.();
        }}
      />

      {/* Data source mode */}
      <SelectInput
        {...p}
        label="Data source mode"
        value={p.vm.cloud_init.dsmode}
        onValueChange={(v) => {
          p.vm.cloud_init.dsmode = v as any;
          p.onChange?.();
        }}
        options={[
          { label: "None", value: undefined },
          { value: "Net" },
          { value: "Local" },
        ]}
      />
    </EditSection>
  );
}

function CloudInitRawUserData(p: CloudInitProps): React.ReactElement {
  return (
    <EditSection
      title="User data"
      actions={
        <RouterLink
          target="_blank"
          to="https://cloudinit.readthedocs.io/en/latest/reference/index.html"
        >
          <Tooltip title="Official reference">
            <IconButton size="small">
              <BookIcon />
            </IconButton>
          </Tooltip>
        </RouterLink>
      }
    >
      <Editor
        theme="vs-dark"
        options={{
          readOnly: !p.editable,
          quickSuggestions: { other: true, comments: true, strings: true },
          wordWrap: "on",
        }}
        language="yaml"
        height={"30vh"}
        value={p.vm.cloud_init.user_data}
        onChange={(v) => {
          p.vm.cloud_init.user_data = v ?? "";
          p.onChange?.();
        }}
      />
    </EditSection>
  );
}

function CloudInitNetworkConfig(p: CloudInitProps): React.ReactElement {
  if (!p.editable && !p.vm.cloud_init.network_configuration) return <></>;
  return (
    <EditSection
      title="Network configuration"
      actions={
        <RouterLink
          target="_blank"
          to="https://cloudinit.readthedocs.io/en/latest/reference/network-config-format-v2.html"
        >
          <Tooltip title="Official network configuration reference">
            <IconButton size="small">
              <BookIcon />
            </IconButton>
          </Tooltip>
        </RouterLink>
      }
    >
      <Editor
        theme="vs-dark"
        options={{
          readOnly: !p.editable,
          quickSuggestions: { other: true, comments: true, strings: true },
          wordWrap: "on",
        }}
        language="yaml"
        height={"30vh"}
        value={p.vm.cloud_init.network_configuration ?? ""}
        onChange={(v) => {
          if (v && v !== "") p.vm.cloud_init.network_configuration = v;
          else p.vm.cloud_init.network_configuration = undefined;
          p.onChange?.();
        }}
      />
    </EditSection>
  );
}

function CloudInitUserDataAssistant(p: CloudInitProps): React.ReactElement {
  const user_data = React.useMemo(() => {
    return YAML.parseDocument(p.vm.cloud_init.user_data);
  }, [p.vm.cloud_init.user_data]);

  const onChange = () => {
    p.vm.cloud_init.user_data = user_data.toString();

    if (!p.vm.cloud_init.user_data.startsWith("#cloud-config"))
      p.vm.cloud_init.user_data = `#cloud-config\n${p.vm.cloud_init.user_data}`;

    p.onChange?.();
  };

  const SYSTEMD_NOT_SERIAL = `/bin/sh -c "rm -f /etc/default/grub.d/50-cloudimg-settings.cfg && sed -i 's/quiet splash//g' /etc/default/grub && update-grub"`;

  return (
    <EditSection title="User data assistant">
      <CloudInitTextInput
        editable={p.editable}
        name="Default user name"
        refUrl="https://cloudinit.readthedocs.io/en/latest/reference/modules.html#set-passwords"
        attrPath={["user", "name"]}
        onChange={onChange}
        yaml={user_data}
      />
      <CloudInitTextInput
        editable={p.editable}
        name="Default user password"
        refUrl="https://cloudinit.readthedocs.io/en/latest/reference/modules.html#set-passwords"
        attrPath={["password"]}
        onChange={onChange}
        yaml={user_data}
      />
      <CloudInitBooleanInput
        editable={p.editable}
        name="Expire password to require new password on next login"
        yaml={user_data}
        attrPath={["chpasswd", "expire"]}
        onChange={onChange}
        refUrl="https://cloudinit.readthedocs.io/en/latest/reference/modules.html#set-passwords"
      />
      <br />
      <CloudInitBooleanInput
        editable={p.editable}
        name="Enable SSH password auth"
        yaml={user_data}
        attrPath={["ssh_pwauth"]}
        onChange={onChange}
        refUrl="https://cloudinit.readthedocs.io/en/latest/reference/modules.html#set-passwords"
      />
      <CloudInitTextInput
        editable={p.editable}
        name="Keyboard layout"
        refUrl="https://cloudinit.readthedocs.io/en/latest/reference/modules.html#keyboard"
        attrPath={["keyboard", "layout"]}
        onChange={onChange}
        yaml={user_data}
      />
      <CloudInitTextInput
        editable={p.editable}
        name="Final message"
        refUrl="https://cloudinit.readthedocs.io/en/latest/reference/modules.html#final-message"
        attrPath={["final_message"]}
        onChange={onChange}
        yaml={user_data}
      />
      {/* /bin/sh -c "rm -f /etc/default/grub.d/50-cloudimg-settings.cfg && update-grub" */}
      <CheckboxInput
        editable={p.editable}
        label="Show all startup messages on tty1, not serial"
        checked={
          !!(user_data.get("runcmd") as any)?.items.find(
            (a: any) => a.value === SYSTEMD_NOT_SERIAL
          )
        }
        onValueChange={(c) => {
          if (!user_data.getIn(["runcmd"])) user_data.addIn(["runcmd"], []);

          const runcmd = user_data.getIn(["runcmd"]) as any;

          if (c) {
            runcmd.addIn([], SYSTEMD_NOT_SERIAL);
          } else {
            const idx = runcmd.items.findIndex(
              (o: any) => o.value === SYSTEMD_NOT_SERIAL
            );
            runcmd.items.splice(idx, 1);
          }
          onChange();
        }}
      />
    </EditSection>
  );
}

function CloudInitTextInput(p: {
  editable: boolean;
  name: string;
  refUrl: string;
  attrPath: Iterable<unknown>;
  yaml: YAML.Document;
  onChange?: () => void;
}): React.ReactElement {
  return (
    <TextInput
      editable={p.editable}
      label={p.name}
      value={String(p.yaml.getIn(p.attrPath) ?? "")}
      onValueChange={(v) => {
        if (v !== undefined) p.yaml.setIn(p.attrPath, v);
        else p.yaml.deleteIn(p.attrPath);
        p.onChange?.();
      }}
      endAdornment={
        <RouterLink to={p.refUrl} target="_blank">
          <IconButton size="small">
            <BookIcon />
          </IconButton>
        </RouterLink>
      }
    />
  );
}

function CloudInitBooleanInput(p: {
  editable: boolean;
  name: string;
  refUrl: string;
  attrPath: Iterable<unknown>;
  yaml: YAML.Document;
  onChange?: () => void;
}): React.ReactElement {
  return (
    <CheckboxInput
      editable={p.editable}
      label={p.name}
      checked={p.yaml.getIn(p.attrPath) === true}
      onValueChange={(v) => {
        if (v) p.yaml.setIn(p.attrPath, v);
        else p.yaml.deleteIn(p.attrPath);
        p.onChange?.();
      }}
    />
  );
}