import {
  Button,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  Typography,
} from "@mui/material";
import Grid from "@mui/material/Grid2";
import { TimePicker } from "@mui/x-date-pickers";
import React from "react";
import { Device, DeviceRelay } from "../api/DeviceApi";
import { RelayApi } from "../api/RelayApi";
import { ServerApi } from "../api/ServerApi";
import { useAlert } from "../hooks/context_providers/AlertDialogProvider";
import { useLoadingMessage } from "../hooks/context_providers/LoadingMessageProvider";
import { useSnackbar } from "../hooks/context_providers/SnackbarProvider";
import { dayjsToTimeOfDay, timeOfDay } from "../utils/DateUtils";
import { lenValid } from "../utils/StringsUtils";
import { CheckboxInput } from "../widgets/forms/CheckboxInput";
import { MultipleSelectInput } from "../widgets/forms/MultipleSelectInput";
import { SelectMultipleRelaysInput } from "../widgets/forms/SelectMultipleRelaysInput";
import { TextInput } from "../widgets/forms/TextInput";

export function EditDeviceRelaysDialog(p: {
  onClose: () => void;
  relay?: DeviceRelay;
  device: Device;
  onUpdated: () => void;
}): React.ReactElement {
  const loadingMessage = useLoadingMessage();
  const alert = useAlert();
  const snackbar = useSnackbar();

  const [relay, setRelay] = React.useState<DeviceRelay>(
    p.relay ?? {
      id: "",
      name: "relay",
      enabled: true,
      priority: 1,
      consumption: 500,
      minimal_downtime: 60 * 5,
      minimal_uptime: 60 * 5,
      depends_on: [],
      conflicts_with: [],
    }
  );

  const creating = !p.relay;

  const onSubmit = async () => {
    try {
      loadingMessage.show(
        `${creating ? "Creating" : "Updating"} relay information`
      );

      if (creating) await RelayApi.Create(p.device, relay);
      else await RelayApi.Update(relay);

      snackbar(
        `The relay have been successfully ${creating ? "created" : "updated"}!`
      );
      p.onUpdated();
    } catch (e) {
      console.error("Failed to update device relay information!" + e);
      alert(`Failed to ${creating ? "create" : "update"} relay! ${e}`);
    } finally {
      loadingMessage.hide();
    }
  };

  const canSubmit =
    lenValid(relay.name, ServerApi.Config.constraints.relay_name_len) &&
    relay.priority >= 0;

  return (
    <Dialog open>
      <DialogTitle>
        {creating ? "Create a new relay" : "Edit relay information"}
      </DialogTitle>
      <DialogContent>
        <DialogFormTitle>General info</DialogFormTitle>
        <Grid container spacing={2}>
          <Grid size={{ xs: 6 }}>
            <TextInput
              editable
              label="Relay name"
              value={relay.name}
              onValueChange={(v) =>
                setRelay((r) => {
                  return {
                    ...r,
                    name: v ?? "",
                  };
                })
              }
              size={ServerApi.Config.constraints.dev_name_len}
            />
          </Grid>
          <Grid size={{ xs: 6 }}>
            <CheckboxInput
              editable
              label="Enable relay"
              checked={relay.enabled}
              onValueChange={(v) =>
                setRelay((r) => {
                  return {
                    ...r,
                    enabled: v,
                  };
                })
              }
            />
          </Grid>
          <Grid size={{ xs: 6 }}>
            <TextInput
              editable
              label="Priority"
              value={relay.priority.toString()}
              type="number"
              onValueChange={(v) =>
                setRelay((r) => {
                  return {
                    ...r,
                    priority: Number(v) ?? 0,
                  };
                })
              }
              size={ServerApi.Config.constraints.relay_priority}
              helperText="Relay priority when selecting relays to turn on. 0 = lowest priority"
            />
          </Grid>
          <Grid size={{ xs: 6 }}>
            <TextInput
              editable
              label="Consumption"
              value={relay.consumption.toString()}
              type="number"
              onValueChange={(v) =>
                setRelay((r) => {
                  return {
                    ...r,
                    consumption: Number(v) ?? 0,
                  };
                })
              }
              size={ServerApi.Config.constraints.relay_consumption}
              helperText="Estimated consumption of device powered by relay"
            />
          </Grid>
          <Grid size={{ xs: 6 }}>
            <TextInput
              editable
              label="Minimal uptime"
              value={relay.minimal_uptime.toString()}
              type="number"
              onValueChange={(v) =>
                setRelay((r) => {
                  return {
                    ...r,
                    minimal_uptime: Number(v) ?? 0,
                  };
                })
              }
              size={ServerApi.Config.constraints.relay_minimal_uptime}
              helperText="Minimal time this relay shall be left on before it can be turned off (in seconds)"
            />
          </Grid>
          <Grid size={{ xs: 6 }}>
            <TextInput
              editable
              label="Minimal downtime"
              value={relay.minimal_downtime.toString()}
              type="number"
              onValueChange={(v) =>
                setRelay((r) => {
                  return {
                    ...r,
                    minimal_downtime: Number(v) ?? 0,
                  };
                })
              }
              size={ServerApi.Config.constraints.relay_minimal_downtime}
              helperText="Minimal time this relay shall be left on before it can be turned off (in seconds)"
            />
          </Grid>
        </Grid>

        <DialogFormTitle>Daily runtime</DialogFormTitle>
        <Grid container spacing={2}>
          <Grid size={{ xs: 6 }}>
            <CheckboxInput
              editable
              label="Enable minimal runtime"
              checked={!!relay.daily_runtime}
              onValueChange={(v) =>
                setRelay((r) => {
                  return {
                    ...r,
                    daily_runtime: v
                      ? { reset_time: 0, min_runtime: 3600, catch_up_hours: [] }
                      : undefined,
                  };
                })
              }
            />
          </Grid>

          {!!relay.daily_runtime && (
            <>
              <Grid size={{ xs: 6 }}>
                <TextInput
                  editable
                  label="Minimal daily runtime"
                  value={relay.daily_runtime!.min_runtime.toString()}
                  type="number"
                  onValueChange={(v) =>
                    setRelay((r) => {
                      return {
                        ...r,
                        daily_runtime: {
                          ...r.daily_runtime!,
                          min_runtime: Number(v),
                        },
                      };
                    })
                  }
                  size={
                    ServerApi.Config.constraints.relay_daily_minimal_runtime
                  }
                  helperText="Minimum time, in seconds, that this relay should run each day"
                />
              </Grid>
              <Grid size={{ xs: 6 }}>
                <TimePicker
                  label="Reset time"
                  value={timeOfDay(relay.daily_runtime!.reset_time)}
                  onChange={(d) =>
                    setRelay((r) => {
                      return {
                        ...r,
                        daily_runtime: {
                          ...r.daily_runtime!,
                          reset_time: d ? dayjsToTimeOfDay(d) : 0,
                        },
                      };
                    })
                  }
                />
              </Grid>
              <Grid size={{ xs: 6 }}>
                <MultipleSelectInput
                  label="Catchup hours"
                  helperText="The hours during which the relay should be turned on to reach expected runtime"
                  values={Array.apply(null, Array(24)).map((_y, i) => {
                    return {
                      label: `${i.toString().padStart(2, "0")}:00`,
                      value: i,
                    };
                  })}
                  selected={relay.daily_runtime!.catch_up_hours}
                  onChange={(d) =>
                    setRelay((r) => {
                      return {
                        ...r,
                        daily_runtime: {
                          ...r.daily_runtime!,
                          catch_up_hours: d,
                        },
                      };
                    })
                  }
                />
              </Grid>
            </>
          )}
        </Grid>

        <DialogFormTitle>Constraints</DialogFormTitle>
        <Grid container spacing={2}>
          <Grid size={{ xs: 6 }}>
            <SelectMultipleRelaysInput
              label="Required relays"
              exclude={[relay.id]}
              value={relay.depends_on}
              onValueChange={(v) =>
                setRelay((r) => {
                  return {
                    ...r,
                    depends_on: v,
                  };
                })
              }
              helperText="Relays that must be already up for this relay to be started"
            />
          </Grid>
          <Grid size={{ xs: 6 }}>
            <SelectMultipleRelaysInput
              label="Conflicting relays"
              exclude={[relay.id]}
              value={relay.conflicts_with}
              onValueChange={(v) =>
                setRelay((r) => {
                  return {
                    ...r,
                    conflicts_with: v,
                  };
                })
              }
              helperText="Relays that must be off before this relay can be started"
            />
          </Grid>
        </Grid>
      </DialogContent>
      <DialogActions>
        <Button onClick={p.onClose}>Cancel</Button>
        <Button onClick={onSubmit} autoFocus disabled={!canSubmit}>
          Submit
        </Button>
      </DialogActions>
    </Dialog>
  );
}

function DialogFormTitle(p: React.PropsWithChildren): React.ReactElement {
  return (
    <>
      <span style={{ height: "2px" }}></span>
      <Typography variant="h6">{p.children}</Typography>
    </>
  );
}
