import React, { useEffect, useState } from "react";

import {
  Table,
  TableBody,
  TableCell,
  TableFooter,
  TablePagination,
  TableRow
} from "@material-ui/core";
import Checkbox from '@mui/material/Checkbox';
import FormControl from '@mui/material/FormControl';
import FormControlLabel from '@mui/material/FormControlLabel';
import FormGroup from '@mui/material/FormGroup';
import FormLabel from '@mui/material/FormLabel';
import LinearProgress from '@mui/material/LinearProgress';

import {
  KeyboardDatePicker, MuiPickersUtilsProvider
} from '@material-ui/pickers';
import clsx from "clsx";
import api from "../../../services/vehiclesAPI";

import DateFnsUtils from '@date-io/date-fns';
import { TableHead } from "@mui/material";
import { logger } from "../../../services/monitoring";
import { LocalDateTimeString } from "../../../utils/dates";
import { useStatusContext } from "../../Contexts/StatusContext";
import { useLocalStorage } from "../../useLocalStorage";
import SearchField from "../../Controls/SearchField";

const tableColumns = [
  {
    id: "level",
    label: "Level",
    width: "5%",
  },
  {
    id: "trex_timestamp",
    label: "T.Rex Timestamp",
    width: "10%",
  },
  {
    id: "cloud_timestamp",
    label: "Cloud Timestamp",
    width: "10%",
  },
  {
    id: "line_number",
    label: "Line Number",
    width: "5%",
  },
  {
    id: "filename",
    label: "Filename",
    width: "10%",
  },
  {
    id: "msg",
    label: "Message",
    width: "60%",
  },
];

const logLevelCheckBoxes = [
  {
    "id": "trace",
    "label": "Trace"
  },
  {
    "id": "debug",
    "label": "Debug"
  },
  {
    "id": "info",
    "label": "Info"
  },
  {
    "id": "warning",
    "label": "Warning"
  },
  {
    "id": "error",
    "label": "Error"
  },
  {
    "id": "critical",
    "label": "Critical"
  },
]

const transformLogs = (logs) =>
  logs.map((log) => {
    const { level, timestamp, received_timestamp, line_number, filename, msg } = log;
    //const trex_time = new Date(timestamp)
    //const cloud_time = new Date(received_timestamp)
    return {
      level: level,
      trex_timestamp: timestamp,
      cloud_timestamp: received_timestamp,
      line_number: line_number,
      filename: filename,
      msg: msg
    };
  })
    .flat()

const formatDateForRequest = (date) => {
  const getYear = date.toLocaleString("default", { year: "numeric" });
  const getMonth = date.toLocaleString("default", { month: "2-digit" });
  const getDay = date.toLocaleString("default", { day: "2-digit" });
  return getYear + "-" + getMonth + "-" + getDay
};

//read at least 50000 bytes per one API request
const ONE_READ_SIZE = 50000
const DEFAULT_PAGE_SIZE = 25

const TRexLogsTable = ({ vin, token, classes }) => {
  const [allLogsFetched, setAllLogsFetched] = useState(false)
  const [blobSize, setBlobSize] = useState(0)
  const [currentOffset, setCurrentOffset] = useState(0)
  const [currentLogLevels, setCurrentLogLevels] = useState({
    trace: true,
    debug: true,
    info: true,
    warning: true,
    error: true,
    critical: true
  })
  const [logs, setLogs] = useState([]);
  const [pageIndex, setPageIndex] = useState(0);
  const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE);
  const local_date = new Date()
  const [selectedDate, setSelectedDate] = useState(new Date(local_date.getUTCFullYear(), local_date.getUTCMonth(), local_date.getUTCDate()));
  const [total, setTotal] = useState(0);
  const [search, setSearch] = useLocalStorage("TREX_LOGS_SEARCH", "");
  const { setMessage } = useStatusContext();

  let controller = new AbortController()
  const readBlob = async (offset, count) => {
    return await api.getTRexLogs(vin, search, formatDateForRequest(selectedDate), offset, count, "UP", token, controller)
  }
  const getDesiredSize = () => {
    return pageSize * pageIndex + pageSize
  }
  const getReadPercentage = () => {
    return (currentOffset * 100 / blobSize);
  }
  const getFilteredLogs = (logs) => {
    return logs.filter(log => currentLogLevels[log.level] === true)
  }
  const fetchAllLogs = async () => {
    let fetched = []
    let offset = currentOffset
    let readSize = ONE_READ_SIZE
    for (; ;) {
      const result = await readBlob(offset, readSize)
      if (result.error) {
        fetched.error = result.error
        break
      }
      setBlobSize(result.blobSize)
      readSize *= 2
      offset = result.RealOffset + result.bytesRead
      setCurrentOffset(offset)
      fetched = transformLogs(result.data).concat(fetched)
      setLogs(fetched)
      if (offset >= result.blobSize) {
        setMessage(`All T.Rex logs for ${formatDateForRequest(selectedDate)} fetched`)
        setAllLogsFetched(true)
        break
      }
    }

    if (fetched.length === 0) {
      if (logs.length !== 0) {
        setMessage(`No more T.Rex logs for ${formatDateForRequest(selectedDate)}`)
        return
      }
      setTotal(0)
      if (fetched.error) {
        setMessage(`Error fetching T.Rex logs for ${formatDateForRequest(selectedDate)}`)
      } else {
        setMessage(`No T.Rex logs for ${formatDateForRequest(selectedDate)}`)
      }
      return
    }
    setCurrentOffset(offset)
    return fetched
  }

  const downloadFile = async () => {
    let day = formatTwoDigit(selectedDate.getDate())
    let month = formatTwoDigit(selectedDate.getMonth() + 1)// 0 Indexed
    let year = selectedDate.getFullYear()

    let result = await api.getLogFileLink({ vin, year, month, day }, token)
    window.open(result['Link'], '_blank')
  }

  const formatTwoDigit = (num) => {
    return num < 10 ? '0' + num : '' + num
  }

  useEffect(() => {
    (async () => {
      try {
        if (!vin || !token) return;
        const desiredSize = getDesiredSize()
        if (desiredSize < logs.length || allLogsFetched) {
          setTotal(getFilteredLogs(logs).length)
          return
        }
        let fetched = await fetchAllLogs()
        if (!fetched || fetched.length === 0) {
          return
        }
        setTotal(getFilteredLogs(fetched).length);
      } catch (e) {
        setMessage(e.message);
        logger.warn(e.stack);
      }
    })();
    return () => controller?.abort()
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [vin, token, search, pageIndex, pageSize, selectedDate, currentLogLevels]);

  const handleChangePageSize = (event) => {
    setPageSize(parseInt(event.target.value, 10));
    setTotal(getFilteredLogs(logs).length)
    setPageIndex(0);
  };

  const handleNewDate = (newValue) => {
    setPageIndex(0);
    setCurrentOffset(0)
    setLogs([])
    setAllLogsFetched(false)
    setBlobSize(0)
    setSelectedDate(newValue)
  };

  const handleNewFilter = (event) => {
    setPageIndex(0)
    setCurrentLogLevels({
      ...currentLogLevels,
      [event.target.defaultValue]: event.target.checked,
    });
  };

  const handleSearch = (query) => {
    if (query !== search) {
      setPageIndex(0);
      setCurrentOffset(0)
      setLogs([])
      setAllLogsFetched(false)
      setBlobSize(0)
      setSearch(query);
    }
  };

  return (
    <div className={clsx(classes.paper, classes.tableSize)}>
      <Table
        style={{ tableLayout: "fixed", width: "100%" }}
      >
        <TableBody>
          <TableRow>
            <TableCell align="center">
              <FormControl component="fieldset">
                <FormLabel component="legend">Log levels</FormLabel>
                <FormGroup aria-label="position" row>
                  {logLevelCheckBoxes.map((box) => (
                    <FormControlLabel
                      key={box.id}
                      value={box.id}
                      control={<Checkbox defaultChecked disabled={!allLogsFetched} />}
                      label={box.label}
                      labelPlacement="bottom"
                      onChange={handleNewFilter}
                    />
                  ))}
                </FormGroup>
              </FormControl>
            </TableCell>

            <TableCell align="center">
              <MuiPickersUtilsProvider utils={DateFnsUtils}>
                <KeyboardDatePicker
                  disableToolbar
                  variant="inline"
                  format="yyyy/MM/dd"
                  margin="normal"
                  id="date-picker-inline"
                  label="Choose date (UTC)"
                  value={selectedDate}
                  onChange={handleNewDate}
                  KeyboardButtonProps={{
                    'aria-label': 'change date',
                  }}
                />
              </MuiPickersUtilsProvider>
            </TableCell>

            <TableCell align="center">
              <SearchField classes={classes} onSearch={handleSearch} savedSearchValue={search} />
            </TableCell>

            <TableCell align="center">
              {
                blobSize === 0 ? `No logs for ${formatDateForRequest(selectedDate)}` :
                  `Read ${getReadPercentage().toFixed(2)}% of logs`
              }
              {
                <LinearProgress
                  variant="determinate"
                  align="center"
                  value={getReadPercentage()} />
              }
              <br></br>
              <button onClick={downloadFile} disabled={blobSize === 0}>Download Log File</button>
            </TableCell>

          </TableRow>
        </TableBody>
      </Table>

      <Table
        style={{ tableLayout: "fixed", width: "100%" }}
      >
        <TableHead classes={classes}>
          <TableRow>
            {tableColumns.map((column) => (
              <TableCell style={{ width: column.width }} align="center" key={column.label || "none"} className={classes.tableHeader} >{column.label}</TableCell>
            ))}
          </TableRow>
        </TableHead >
        <TableBody>
          {getFilteredLogs(logs).slice(-getDesiredSize(), (pageIndex === 0 ? undefined : -(pageSize * pageIndex))).reverse().map((log, i) => (
            <TableRow key={log.trex_timestamp + log.cloud_timestamp} >
              <TableCell align="center">{log.level}</TableCell>
              <TableCell align="center" style={{ wordBreak: "break-all" }}>{LocalDateTimeString(log.trex_timestamp)}</TableCell>
              <TableCell align="center" style={{ wordBreak: "break-all" }}>{LocalDateTimeString(log.cloud_timestamp)}</TableCell>
              <TableCell align="center">{log.line_number}</TableCell>
              <TableCell align="center" style={{ wordBreak: "break-all" }}>{log.filename}</TableCell>
              <TableCell align="left" style={{ wordBreak: "break-all" }}>{log.msg}</TableCell>
            </TableRow>
          ))}
        </TableBody>
        <TableFooter>
          <TableRow>
            <TablePagination
              rowsPerPageOptions={[25, 100, 1000, 10000]}
              colSpan={6}
              count={total}
              rowsPerPage={pageSize}
              page={!pageIndex || pageIndex <= 0 ? 0 : pageIndex}
              SelectProps={{
                inputProps: { "aria-label": "rows per page" },
                native: true,
              }}
              onPageChange={(_, newValue) => setPageIndex(newValue)}
              onRowsPerPageChange={handleChangePageSize}
            />
          </TableRow>
        </TableFooter>
      </Table>
    </div >
  );
};

export default TRexLogsTable;
