import DeleteIcon from "@mui/icons-material/Delete";
import DownloadIcon from "@mui/icons-material/Download";
import MenuBookIcon from "@mui/icons-material/MenuBook";
import RefreshIcon from "@mui/icons-material/Refresh";
import {
  Alert,
  Button,
  CircularProgress,
  IconButton,
  LinearProgress,
  TextField,
  Tooltip,
  Typography,
} from "@mui/material";
import { DataGrid, GridColDef } from "@mui/x-data-grid";
import { filesize } from "filesize";
import React from "react";
import { IsoFile, IsoFilesApi } from "../api/IsoFilesApi";
import { ServerApi } from "../api/ServerApi";
import { IsoCatalogDialog } from "../dialogs/IsoCatalogDialog";
import { useAlert } from "../hooks/providers/AlertDialogProvider";
import { useConfirm } from "../hooks/providers/ConfirmDialogProvider";
import { useLoadingMessage } from "../hooks/providers/LoadingMessageProvider";
import { useSnackbar } from "../hooks/providers/SnackbarProvider";
import { downloadBlob } from "../utils/FilesUtils";
import { AsyncWidget } from "../widgets/AsyncWidget";
import { FileInput } from "../widgets/forms/FileInput";
import { VirtWebPaper } from "../widgets/VirtWebPaper";
import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer";

export function IsoFilesRoute(): React.ReactElement {
  const [list, setList] = React.useState<IsoFile[] | undefined>();
  const [isoCatalog, setIsoCatalog] = React.useState(false);

  const loadKey = React.useRef(1);

  const load = async () => {
    setList(await IsoFilesApi.GetList());
  };

  const reload = () => {
    loadKey.current += 1;
    setList(undefined);
  };

  return (
    <>
      <AsyncWidget
        loadKey={loadKey.current}
        errMsg="Failed to load ISO files list!"
        load={load}
        ready={list !== undefined}
        build={() => (
          <VirtWebRouteContainer
            label="ISO files management"
            actions={
              <span>
                <Tooltip title="Open the ISO catalog">
                  <IconButton onClick={() => { setIsoCatalog(true); }}>
                    <MenuBookIcon />
                  </IconButton>
                </Tooltip>
                <Tooltip title="Refresh ISO list">
                  <IconButton onClick={reload}>
                    <RefreshIcon />
                  </IconButton>
                </Tooltip>
              </span>
            }
          >
            <UploadIsoFileCard onFileUploaded={reload} />
            <UploadIsoFileFromUrlCard onFileUploaded={reload} />
            <IsoFilesList list={list!} onReload={reload} />
          </VirtWebRouteContainer>
        )}
      />
      <IsoCatalogDialog
        open={isoCatalog}
        onClose={() => { setIsoCatalog(false); }}
      />
    </>
  );
}

function UploadIsoFileCard(p: {
  onFileUploaded: () => void;
}): React.ReactElement {
  const alert = useAlert();
  const snackbar = useSnackbar();

  const [value, setValue] = React.useState<File | null>(null);
  const [uploadProgress, setUploadProgress] = React.useState<number | null>(
    null
  );

  const handleChange = (newValue: File | null) => {
    if (newValue && newValue.size > ServerApi.Config.constraints.iso_max_size) {
      alert(
        `The file is too big (max size allowed: ${filesize(
          ServerApi.Config.constraints.iso_max_size
        )}`
      );
      return;
    }

    if (newValue && !ServerApi.Config.iso_mimetypes.includes(newValue.type)) {
      alert(`Selected file mimetype is not allowed! (${newValue.type})`);
      return;
    }

    setValue(newValue);
  };

  const upload = async () => {
    try {
      setUploadProgress(0);
      await IsoFilesApi.Upload(value!, setUploadProgress);

      setValue(null);
      snackbar("The file was successfully uploaded!");

      p.onFileUploaded();
    } catch (e) {
      console.error(e);
      await alert(`Failed to perform file upload! ${e}`);
    }

    setUploadProgress(null);
  };

  if (uploadProgress !== null) {
    return (
      <VirtWebPaper label="File upload" noHorizontalMargin>
        <Typography variant="body1">
          Upload in progress ({Math.floor(uploadProgress * 100)}%)...
        </Typography>
        <LinearProgress variant="determinate" value={uploadProgress * 100} />
      </VirtWebPaper>
    );
  }

  return (
    <VirtWebPaper label="File upload" noHorizontalMargin>
      <div style={{ display: "flex", alignItems: "center" }}>
        <FileInput
          value={value}
          onChange={handleChange}
          style={{ flex: 1 }}
          slotProps={{
            htmlInput: { accept: ServerApi.Config.iso_mimetypes.join(",") },
          }}
        />

        {value && <Button onClick={upload}>Upload file</Button>}
      </div>
    </VirtWebPaper>
  );
}

function UploadIsoFileFromUrlCard(p: {
  onFileUploaded: () => void;
}): React.ReactElement {
  const alert = useAlert();
  const snackbar = useSnackbar();
  const loadingMessage = useLoadingMessage();

  const [url, setURL] = React.useState("");
  const [filename, setFilename] = React.useState<null | string>(null);

  const autoFileName = url.split("/").slice(-1)[0];
  const actualFileName = filename ?? autoFileName;

  const upload = async () => {
    try {
      loadingMessage.show("Downloading file from URL...");
      await IsoFilesApi.UploadFromURL(url, actualFileName);

      p.onFileUploaded();

      setURL("");
      setFilename(null);
      snackbar("Successfully downloaded file!");
    } catch (e) {
      console.error(e);
      alert("Failed to download file!");
    }
    loadingMessage.hide();
  };

  return (
    <VirtWebPaper label="File upload from URL" noHorizontalMargin>
      <div style={{ display: "flex", alignItems: "center" }}>
        <TextField
          label="URL"
          value={url}
          style={{ flex: 3 }}
          onChange={(e) => {
            setURL(e.target.value);
          }}
        />
        <span style={{ width: "10px" }}></span>
        <TextField
          label="Filename"
          value={actualFileName}
          style={{ flex: 2 }}
          onChange={(e) => {
            setFilename(e.target.value);
          }}
        />
        {url !== "" && actualFileName !== "" && (
          <Button onClick={upload}>Upload file</Button>
        )}
      </div>
    </VirtWebPaper>
  );
}

function IsoFilesList(p: {
  list: IsoFile[];
  onReload: () => void;
}): React.ReactElement {
  const confirm = useConfirm();
  const alert = useAlert();
  const loadingMessage = useLoadingMessage();
  const snackbar = useSnackbar();

  const [dlProgress, setDlProgress] = React.useState<undefined | number>();

  const downloadIso = async (entry: IsoFile) => {
    setDlProgress(0);

    try {
      const blob = await IsoFilesApi.Download(entry, setDlProgress);

      downloadBlob(blob, entry.filename);
    } catch (e) {
      console.error(e);
      alert("Failed to download iso file!");
    }

    setDlProgress(undefined);
  };

  const deleteIso = async (entry: IsoFile) => {
    if (
      !(await confirm(
        `Do you really want to delete this file (${entry.filename}) ?`
      ))
    )
      return;

    loadingMessage.show("Deleting ISO file...");

    try {
      await IsoFilesApi.Delete(entry);
      snackbar("The file has been successfully deleted!");
      p.onReload();
    } catch (e) {
      console.error(e);
      alert(`Failed to delete file!\n${e}`);
    }

    loadingMessage.hide();
  };

  if (p.list.length === 0)
    return (
      <Typography variant="body1" style={{ textAlign: "center" }}>
        No ISO file uploaded for now.
      </Typography>
    );

  const columns: GridColDef<IsoFile>[] = [
    { field: "filename", headerName: "File name", flex: 3 },
    {
      field: "size",
      headerName: "File size",
      flex: 1,
      renderCell(params) {
        return filesize(params.row.size);
      },
    },
    {
      field: "actions",
      headerName: "",
      width: 120,
      renderCell(params) {
        return (
          <>
            <Tooltip title="Download file">
              <IconButton onClick={() => downloadIso(params.row)}>
                <DownloadIcon />
              </IconButton>
            </Tooltip>
            <Tooltip title="Delete file">
              <IconButton onClick={() => deleteIso(params.row)}>
                <DeleteIcon />
              </IconButton>
            </Tooltip>
          </>
        );
      },
    },
  ];

  return (
    <>
      {/* Download notification */}
      {dlProgress !== undefined && (
        <Alert severity="info">
          <div
            style={{
              display: "flex",
              flexDirection: "row",
              alignItems: "center",
              overflow: "hidden",
            }}
          >
            <Typography variant="body1">
              Downloading... {dlProgress}%
            </Typography>
            <CircularProgress
              variant="determinate"
              size={"1.5rem"}
              style={{ marginLeft: "10px" }}
              value={dlProgress}
            />
          </div>
        </Alert>
      )}
      {/* ISO files list table */}
      <DataGrid getRowId={(c) => c.filename} rows={p.list} columns={columns} />
    </>
  );
}