// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

import React, { Fragment, useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
import { Button, Grid } from "@mui/material";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import moment from "moment/moment";
import PageHeader from "../Common/PageHeader/PageHeader";
import {
  actionsTray,
  advancedFilterToggleStyles,
  containerForHeader,
  formFieldStyles,
  searchField,
} from "../Common/FormComponents/common/styleLibrary";
import { wsProtocol } from "../../../utils/wsUtils";
import { SpeedTestResponse } from "./types";
import { SpeedtestIcon } from "../../../icons";
import {
  CONSOLE_UI_RESOURCE,
  IAM_SCOPES,
} from "../../../common/SecureComponent/permissions";
import STResults from "./STResults";
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import ProgressBarWrapper from "../Common/ProgressBarWrapper/ProgressBarWrapper";
import InputUnitMenu from "../Common/FormComponents/InputUnitMenu/InputUnitMenu";
import PageLayout from "../Common/Layout/PageLayout";
import { SecureComponent } from "../../../common/SecureComponent";
import DistributedOnly from "../Common/DistributedOnly/DistributedOnly";
import HelpBox from "../../../common/HelpBox";
import WarnIcon from "../../../icons/WarnIcon";
import Loader from "../Common/Loader/Loader";
import { selDistSet } from "../../../systemSlice";
import makeStyles from "@mui/styles/makeStyles";

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    advancedContent: {
      backgroundColor: "#FBFAFA",
      maxHeight: 0,
      transitionDuration: "0.3s",
      overflow: "hidden",
      padding: "0 15px",
      marginTop: 15,
      justifyContent: "space-between",
      "&.open": {
        maxHeight: 400,
        paddingBottom: 15,
      },
    },

    stepProgressText: {
      fontSize: 13,
      marginBottom: 8,
    },
    ...advancedFilterToggleStyles,
    ...actionsTray,
    ...searchField,
    ...formFieldStyles,
    ...containerForHeader(theme.spacing(4)),
  })
);

const Speedtest = () => {
  const distributedSetup = useSelector(selDistSet);
  const classes = useStyles();
  const [start, setStart] = useState<boolean>(false);

  const [currStatus, setCurrStatus] = useState<SpeedTestResponse[] | null>(
    null
  );

  const [size, setSize] = useState<string>("64");
  const [sizeUnit, setSizeUnit] = useState<string>("MB");
  const [duration, setDuration] = useState<string>("10");

  const [topDate, setTopDate] = useState<number>(0);
  const [currentValue, setCurrentValue] = useState<number>(0);
  const [totalSeconds, setTotalSeconds] = useState<number>(0);
  const [speedometerValue, setSpeedometerValue] = useState<number>(0);

  useEffect(() => {
    // begin watch if bucketName in bucketList and start pressed
    if (start) {
      const url = new URL(window.location.toString());
      const isDev = process.env.NODE_ENV === "development";
      const port = isDev ? "9090" : url.port;

      // check if we are using base path, if not this always is `/`
      const baseLocation = new URL(document.baseURI);
      const baseUrl = baseLocation.pathname;

      const wsProt = wsProtocol(url.protocol);
      const c = new W3CWebSocket(
        `${wsProt}://${url.hostname}:${port}${baseUrl}ws/speedtest?&size=${size}${sizeUnit}&duration=${duration}s`
      );

      const baseDate = moment();

      const currentTime = baseDate.unix() / 1000;

      const incrementDate =
        baseDate
          .add(parseInt("10") * 2, "s" as moment.unitOfTime.DurationConstructor)
          .unix() / 1000;

      const totalSeconds = (incrementDate - currentTime) / 1000;

      setTopDate(incrementDate);
      setCurrentValue(currentTime);
      setTotalSeconds(totalSeconds);

      let interval: any | null = null;
      if (c !== null) {
        c.onopen = () => {
          console.log("WebSocket Client Connected");
          c.send("ok");
          interval = setInterval(() => {
            c.send("ok");
          }, 10 * 1000);
        };
        c.onmessage = (message: IMessageEvent) => {
          const data: SpeedTestResponse = JSON.parse(message.data.toString());

          setCurrStatus((prevStatus) => {
            let prSt: SpeedTestResponse[] = [];
            if (prevStatus) {
              prSt = [...prevStatus];
            }

            const insertData = data.servers !== 0 ? [data] : [];
            return [...prSt, ...insertData];
          });

          const currTime = moment().unix() / 1000;
          setCurrentValue(currTime);
        };
        c.onclose = () => {
          clearInterval(interval);
          console.log("connection closed by server");
          // reset start status
          setStart(false);
        };
        return () => {
          // close websocket on useEffect cleanup
          c.close(1000);
          clearInterval(interval);
          console.log("closing websockets");
        };
      }
    } else {
      // reset start status
      setStart(false);
    }
  }, [size, sizeUnit, start, duration]);

  useEffect(() => {
    const actualSeconds = (topDate - currentValue) / 1000;

    let percToDisplay = 100 - (actualSeconds * 100) / totalSeconds;

    if (percToDisplay > 100) {
      percToDisplay = 100;
    }

    setSpeedometerValue(percToDisplay);
  }, [start, currentValue, topDate, totalSeconds]);

  return (
    <Fragment>
      <PageHeader label="Performance" />
      <PageLayout>
        {!distributedSetup ? (
          <DistributedOnly
            iconComponent={<SpeedtestIcon />}
            entity={"Speedtest"}
          />
        ) : (
          <SecureComponent
            scopes={[IAM_SCOPES.ADMIN_HEAL]}
            resource={CONSOLE_UI_RESOURCE}
          >
            <Grid item xs={12} className={classes.boxy}>
              <Grid container>
                <Grid item md={4} sm={12}>
                  <div className={classes.stepProgressText}>
                    {start ? (
                      <Fragment>
                        Speedtest in progress...
                        <Loader style={{ width: 15, height: 15 }} />
                      </Fragment>
                    ) : (
                      <Fragment>
                        {currStatus && !start ? (
                          <b>Speed Test results:</b>
                        ) : (
                          <b>Performance test</b>
                        )}
                      </Fragment>
                    )}
                  </div>
                  <div>
                    <ProgressBarWrapper
                      value={speedometerValue}
                      ready={currStatus !== null && !start}
                      indeterminate={start}
                      size={"small"}
                    />
                  </div>
                </Grid>
                <Grid item md sm={12}>
                  <div style={{ marginLeft: 10, width: 300 }}>
                    <InputBoxWrapper
                      id={"size"}
                      name={"size"}
                      label={"Object Size"}
                      onChange={(e) => {
                        setSize(e.target.value);
                      }}
                      noLabelMinWidth={true}
                      value={size}
                      disabled={start}
                      overlayObject={
                        <InputUnitMenu
                          id={"size-unit"}
                          onUnitChange={setSizeUnit}
                          unitSelected={sizeUnit}
                          unitsList={[
                            { label: "KiB", value: "KiB" },
                            { label: "MiB", value: "MiB" },
                            { label: "GiB", value: "GiB" },
                          ]}
                          disabled={start}
                        />
                      }
                    />
                  </div>
                </Grid>
                <Grid item md sm={12}>
                  <div style={{ marginLeft: 10, width: 300 }}>
                    <InputBoxWrapper
                      id={"duration"}
                      name={"duration"}
                      label={"Duration"}
                      onChange={(e) => {
                        if (e.target.validity.valid) {
                          setDuration(e.target.value);
                        }
                      }}
                      noLabelMinWidth={true}
                      value={duration}
                      disabled={start}
                      overlayObject={
                        <InputUnitMenu
                          id={"size-unit"}
                          onUnitChange={() => {}}
                          unitSelected={"s"}
                          unitsList={[{ label: "s", value: "s" }]}
                          disabled={start}
                        />
                      }
                      pattern={"[0-9]*"}
                    />
                  </div>
                </Grid>
                <Grid item md={1} sm={12} textAlign={"right"}>
                  <Button
                    onClick={() => {
                      setCurrStatus(null);
                      setStart(true);
                    }}
                    color="primary"
                    type="button"
                    id={"start-speed-test"}
                    variant={
                      currStatus !== null && !start ? "contained" : "outlined"
                    }
                    className={`${classes.buttonBackground} ${classes.speedStart}`}
                    disabled={
                      duration.trim() === "" || size.trim() === "" || start
                    }
                  >
                    {!start && (
                      <Fragment>
                        {currStatus !== null ? "Retest" : "Start"}
                      </Fragment>
                    )}
                    {start ? "Start" : ""}
                  </Button>
                </Grid>
              </Grid>
              <Grid container className={classes.multiModule}>
                <Grid item xs={12}>
                  <Fragment>
                    <Grid item xs={12}>
                      {currStatus !== null && (
                        <Fragment>
                          <STResults results={currStatus} start={start} />
                        </Fragment>
                      )}
                    </Grid>
                  </Fragment>
                </Grid>
              </Grid>
            </Grid>

            {!start && !currStatus && (
              <Fragment>
                <br />
                <HelpBox
                  title={
                    "During the speed test all your production traffic will be temporarily suspended."
                  }
                  iconComponent={<WarnIcon />}
                  help={<Fragment />}
                />
              </Fragment>
            )}
          </SecureComponent>
        )}
      </PageLayout>
    </Fragment>
  );
};

export default Speedtest;
