import { Button, Checkbox, Grid } from "@mui/material";
import React from "react";
import { useNavigate } from "react-router-dom";
import { IpConfig, NetworkApi, NetworkInfo } from "../../api/NetworksApi";
import { ServerApi } from "../../api/ServerApi";
import { useAlert } from "../../hooks/providers/AlertDialogProvider";
import { useConfirm } from "../../hooks/providers/ConfirmDialogProvider";
import { useSnackbar } from "../../hooks/providers/SnackbarProvider";
import { AsyncWidget } from "../AsyncWidget";
import { TabsWidget } from "../TabsWidget";
import { XMLAsyncWidget } from "../XMLWidget";
import { EditSection } from "../forms/EditSection";
import { IPInput } from "../forms/IPInput";
import { NetDHCPHostReservations } from "../forms/NetDHCPHostReservations";
import { NetNatConfiguration } from "../forms/NetNatConfiguration";
import { ResAutostartInput } from "../forms/ResAutostartInput";
import { SelectInput } from "../forms/SelectInput";
import { TextInput } from "../forms/TextInput";

interface DetailsProps {
  net: NetworkInfo;
  editable: boolean;
  onChange?: () => void;
}

export function NetworkDetails(p: DetailsProps): React.ReactElement {
  const [nicsList, setNicsList] = React.useState<string[] | any>();

  const load = async () => {
    setNicsList(await ServerApi.GetNetworksList());
  };

  return (
    <AsyncWidget
      loadKey={"1"}
      load={load}
      errMsg="Failed to load the list of host network cards!"
      build={() => <NetworkDetailsInner nicsList={nicsList} {...p} />}
    />
  );
}

enum NetTab {
  General = 0,
  IPv4,
  IPv6,
  XML,
  Danger,
}

type DetailsInnerProps = DetailsProps & { nicsList: string[] };

function NetworkDetailsInner(p: DetailsInnerProps): React.ReactElement {
  const [currTab, setCurrTab] = React.useState(NetTab.General);

  return (
    <>
      <TabsWidget
        currTab={currTab}
        onTabChange={setCurrTab}
        options={[
          { label: "General", value: NetTab.General, visible: true },
          {
            label: "IPv4",
            value: NetTab.IPv4,
            visible: p.editable || !!p.net.ip_v4,
          },
          {
            label: "IPv6",
            value: NetTab.IPv6,
            visible: p.editable || !!p.net.ip_v6,
          },
          {
            label: "XML",
            value: NetTab.XML,
            visible: !p.editable,
          },
          {
            label: "Danger zone",
            value: NetTab.Danger,
            color: "red",
            visible: !p.editable,
          },
        ]}
      />

      {currTab === NetTab.General && <NetworkDetailsTabGeneral {...p} />}
      {currTab === NetTab.IPv4 && <NetworkDetailsTabIPv4 {...p} />}
      {currTab === NetTab.IPv6 && <NetworkDetailsTabIPv6 {...p} />}
      {currTab === NetTab.XML && <NetworkDetailsTabXML {...p} />}
      {currTab === NetTab.Danger && <NetworkDetailsTabDanger {...p} />}
    </>
  );
}

function NetworkDetailsTabGeneral(p: DetailsInnerProps): React.ReactElement {
  return (
    <Grid container spacing={2}>
      {/* Metadata section */}
      <EditSection title="Metadata">
        <TextInput
          label="Name"
          editable={p.editable && !p.net.uuid}
          value={p.net.name}
          onValueChange={(v) => {
            p.net.name = v ?? "";
            p.onChange?.();
          }}
          checkValue={(v) => /^[a-zA-Z0-9]+$/.test(v)}
          size={ServerApi.Config.constraints.net_name_size}
        />

        <TextInput label="UUID" editable={false} value={p.net.uuid} />

        <TextInput
          label="Title"
          editable={p.editable}
          value={p.net.title}
          onValueChange={(v) => {
            p.net.title = v;
            p.onChange?.();
          }}
          size={ServerApi.Config.constraints.net_title_size}
        />

        <TextInput
          label="Description"
          editable={p.editable}
          value={p.net.description}
          onValueChange={(v) => {
            p.net.description = v;
            p.onChange?.();
          }}
          multiline={true}
        />

        {p.net.uuid && (
          <ResAutostartInput
            editable={p.editable}
            ressourceName="Network"
            checkAutotostart={() => NetworkApi.IsAutostart(p.net)}
            setAutotostart={(e) => NetworkApi.SetAutostart(p.net, e)}
          />
        )}
      </EditSection>

      <EditSection title="General settings">
        <SelectInput
          editable={p.editable}
          label="Forward mode"
          onValueChange={(v) => {
            p.net.forward_mode = v as any;
            p.onChange?.();
          }}
          value={p.net.forward_mode}
          options={[
            {
              label: "NAT",
              value: "NAT",
            },

            {
              label: "Isolated network",
              value: "Isolated",
            },
          ]}
        />

        {p.net.forward_mode === "NAT" && (
          <SelectInput
            editable={p.editable}
            label="Network output device"
            onValueChange={(v) => {
              p.net.device = v;
              p.onChange?.();
            }}
            value={p.net.device}
            options={[
              { label: "Default interface", value: undefined },
              ...p.nicsList.map((d) => {
                return { label: d, value: d };
              }),
            ]}
          />
        )}

        <TextInput
          editable={p.editable}
          label="Network bridge name"
          onValueChange={(v) => {
            p.net.bridge_name = v;
            p.onChange?.();
          }}
          value={p.net.bridge_name}
          checkValue={(v) => v === "" || /^[a-zA-Z0-9]+$/.test(v)}
        />

        <IPInput
          editable={p.editable}
          label="DNS server to use"
          value={p.net.dns_server}
          onValueChange={(v) => {
            p.net.dns_server = v;
            p.onChange?.();
          }}
          version={4}
        />

        <TextInput
          label="Domain"
          editable={p.editable}
          value={p.net.domain}
          onValueChange={(v) => {
            p.net.domain = v;
            p.onChange?.();
          }}
          multiline={true}
        />
      </EditSection>
    </Grid>
  );
}

function NetworkDetailsTabIPv4(p: DetailsInnerProps): React.ReactElement {
  return (
    <IPSection
      {...p}
      config={p.net.ip_v4}
      onChange={(c) => {
        p.net.ip_v4 = c;
        p.onChange?.();
      }}
      version={4}
    />
  );
}

function NetworkDetailsTabIPv6(p: DetailsInnerProps): React.ReactElement {
  return (
    <IPSection
      {...p}
      config={p.net.ip_v6}
      onChange={(c) => {
        p.net.ip_v6 = c;
        p.onChange?.();
      }}
      version={6}
    />
  );
}

function IPSection(p: {
  editable: boolean;
  config?: IpConfig;
  onChange: (c: IpConfig | undefined) => void;
  version: 4 | 6;
  nicsList: string[];
}): React.ReactElement {
  const confirm = useConfirm();

  const toggleNetwork = async () => {
    if (!!p.config) {
      if (
        !(await confirm(
          `Do you really want to disable IPv${p.version} on this network? Specific configuration will be deleted!`
        ))
      )
        return;

      p.onChange?.(undefined);
      return;
    }

    p.onChange?.({
      bridge_address: p.version === 4 ? "192.168.1.1" : "fd00::1",
      prefix: p.version === 4 ? 24 : 8,
    });
  };

  const toggleDHCP = async (v: boolean) => {
    if (v) {
      p.config!.dhcp =
        p.version === 4
          ? {
              start: "192.168.1.100",
              end: "192.168.1.200",
              hosts: [],
            }
          : { start: "fd00::100", end: "fd00::f00", hosts: [] };
    } else {
      if (
        !(await confirm(
          `Do you really want to disable DHCPv${p.version} on this network? Specific configuration will be deleted!`
        ))
      )
        return;
      p.config!.dhcp = undefined;
    }

    p.onChange?.(p.config);
  };

  const toggleNAT = async (v: boolean) => {
    if (v) {
      p.config!.nat = [];
    } else {
      if (
        (p.config?.nat?.length ?? 0 > 0) &&
        !(await confirm(
          `Do you really want to disable IPv${p.version} NAT port forwarding on this network? Specific configuration will be deleted!`
        ))
      )
        return;
      p.config!.nat = undefined;
    }

    p.onChange?.(p.config);
  };

  if (!p.config && !p.editable) return <></>;

  return (
    <Grid container spacing={2}>
      <EditSection
        title={`IPv${p.version} network`}
        actions={
          <Checkbox
            disabled={!p.editable}
            checked={!!p.config}
            onChange={toggleNetwork}
          />
        }
      >
        {p.config && (
          <>
            <IPInput
              editable={p.editable}
              label="Bridge address"
              version={p.version}
              value={p.config?.bridge_address}
              onValueChange={(v) => {
                p.config!.bridge_address = v ?? "";
                p.onChange?.(p.config);
              }}
            />

            <TextInput
              label="Prefix"
              editable={p.editable}
              value={p.config.prefix.toString()}
              type="number"
              onValueChange={(v) => {
                p.config!.prefix = Number(v);
                p.onChange?.(p.config);
              }}
              size={
                p.version === 4 ? { min: 0, max: 32 } : { min: 0, max: 128 }
              }
            />
          </>
        )}
      </EditSection>

      {p.config && (p.editable || p.config.dhcp) && (
        <EditSection
          title={`DHCP v${p.version}`}
          actions={
            <Checkbox
              disabled={!p.editable}
              checked={!!p.config.dhcp}
              onChange={(_ev, val) => toggleDHCP(val)}
            />
          }
        >
          {p.config.dhcp && (
            <>
              <IPInput
                label="DHCP allocation start"
                editable={p.editable}
                version={p.version}
                value={p.config.dhcp.start}
                onValueChange={(v) => {
                  p.config!.dhcp!.start = v!;
                  p.onChange(p.config);
                }}
              />
              <IPInput
                label="DHCP allocation end"
                editable={p.editable}
                version={p.version}
                value={p.config.dhcp.end}
                onValueChange={(v) => {
                  p.config!.dhcp!.end = v!;
                  p.onChange(p.config);
                }}
              />
            </>
          )}
        </EditSection>
      )}

      {p.config?.dhcp && (p.editable || p.config.dhcp.hosts.length > 0) && (
        <EditSection title="DHCP hosts reservations" fullWidth>
          <NetDHCPHostReservations
            {...p}
            dhcp={p.config.dhcp}
            onChange={(d) => {
              p.config!.dhcp = d;
              p.onChange?.(p.config);
            }}
          />
        </EditSection>
      )}

      {p.config && (p.editable || p.config.nat) && (
        <EditSection
          title={`NAT v${p.version} ports redirection`}
          fullWidth
          actions={
            <Checkbox
              disabled={!p.editable}
              checked={!!p.config.nat}
              onChange={(_ev, val) => toggleNAT(val)}
            />
          }
        >
          {p.config.nat && (
            <NetNatConfiguration
              {...p}
              nat={p.config.nat}
              onChange={(n) => {
                p.config!.nat = n;
                p.onChange?.(p.config);
              }}
            />
          )}
        </EditSection>
      )}
    </Grid>
  );
}

function NetworkDetailsTabXML(p: DetailsInnerProps): React.ReactElement {
  return (
    <XMLAsyncWidget
      errMsg="Failed to load network XML definition!"
      identifier={p.net.uuid!}
      load={() => NetworkApi.GetSingleXML(p.net.uuid!)}
    />
  );
}

function NetworkDetailsTabDanger(p: DetailsInnerProps): React.ReactElement {
  const confirm = useConfirm();
  const snackbar = useSnackbar();
  const alert = useAlert();
  const navigate = useNavigate();

  const requestDelete = async () => {
    try {
      if (
        !(await confirm(
          "Do you really want to delete this network?",
          `Delete network ${p.net.name}`,
          "Delete"
        ))
      )
        return;

      await NetworkApi.Delete(p.net);

      navigate("/net");
      snackbar("The network was successfully deleted!");
    } catch (e) {
      console.error(e);
      alert(`Failed to delete the network!\n${e}`);
    }
  };

  return (
    <Button color="error" onClick={requestDelete}>
      Delete this network
    </Button>
  );
}
