import styles from './styles.module.css'
import {useEffect, useState} from "react"
import DeleteForeverOutlined from "@mui/icons-material/DeleteForeverOutlined"
import StopCircleOutlined from "@mui/icons-material/StopCircleOutlined"
import PauseCircleOutline from "@mui/icons-material/PauseCircleOutline"
import PlayCircleOutline from "@mui/icons-material/PlayCircleOutline"
import HourglassEmptyOutlined from "@mui/icons-material/HourglassEmptyOutlined"
import CheckCircleOutline from "@mui/icons-material/CheckCircleOutline"
import ErrorOutline from "@mui/icons-material/ErrorOutline"
import ReplayOutlined from "@mui/icons-material/ReplayOutlined"
import RunCircleOutlined from "@mui/icons-material/RunCircleOutlined"
import FactCheckOutlined from "@mui/icons-material/FactCheckOutlined"
import {IconButton, LinearProgress, TableContainer, Table, TableBody, TableCell, TableHead, TableRow, TablePagination} from "@mui/material"
import ConfirmDialog from "../confirm-dialog";
import smClient from "../../../api/sm/sm-client";

export interface ProcessExceptionInterface {
    message: string,
    file: string,
    line: number,
    code: number,
    trace: any[],
}

export interface ProcessProgressInterface {
    total: number,
    progress: number,
    memory: any[],
}

export interface ProcessInterface {
    id: string,
    lock: string | null,
    containerUuid: string | null,
    pid: bigint | null,
    name: string,
    options: Record<string, any>,
    arguments: Record<string, any>,
    exception: ProcessExceptionInterface | null,
    progress: ProcessProgressInterface | null,
    outputId: string | null,
    createdAt: string,
    updatedAt: string | null,
    startedAt: string | null,
    pausedAt: string | null,
    cancelledAt: string | null
    completedAt: string | null
}

enum Status {
    Wait,
    Running,
    Cancelled,
    Error,
    Success
}

export default function Processes() {
    const [processes, setProcesses] = useState<ProcessInterface[]>([]);
    const [page, setPage] = useState<number>(0);
    const [count, setCount] = useState<number>(0);
    const [open, setOpen] = useState<boolean>(false);
    const [selectedProcess, setSelectedProcess] = useState<ProcessInterface | null>(null);

    let refreshLock = false
    let refreshProcesses = async () => {
        if (refreshLock) {
            return
        }
        refreshLock = true
        const { data: processes, headers } = await smClient.getProcesses({
            page: page + 1,
            limit: 20,
        })
        setProcesses(processes)
        setCount(Number(headers.get('X-Pagination-Count')))
        refreshLock = false
    }
    let output = async (process: ProcessInterface) => {
        let response = await fetch(`http://fmw.sipachev.sv/system-monitoring/processes/${process.id}/output`,
            {
                method: 'GET',
                headers: {
                    'X-Plugin-Token': 'passw0rd',
                    'X-Pagination-Count': '0',
                },
            });
        const output: string = await response.text()
        let a = document.createElement("a");
        let file = new Blob([output], {type: 'plain/text'});
        a.href = URL.createObjectURL(file);
        a.download = `${process.id}.txt`;
        a.click();
    }

    useEffect(() => {
        const timer = setInterval(() => refreshProcesses(), 1000)
        return () => clearInterval(timer);
    }, [page]);

    let isFinished = (process: ProcessInterface) => process.cancelledAt || process.completedAt
    let canPlay = (process: ProcessInterface) => !isFinished(process) && process.progress && process.pausedAt
    let canPause = (process: ProcessInterface) => !isFinished(process) && process.progress && !process.pausedAt
    let canRepeat = (process: ProcessInterface) => isFinished(process)
    let canStop = (process: ProcessInterface) => !isFinished(process) && process.progress
    let canKill = (process: ProcessInterface) => !isFinished(process) && process.startedAt
    let progress = (progress: ProcessProgressInterface) => progress.progress / progress.total * 100

    let status = (process: ProcessInterface): Status => {
        if (process.cancelledAt && process.completedAt) {
            return Status.Cancelled
        }
        if (process.exception && process.completedAt) {
            return Status.Error
        }
        if (!process.exception && process.completedAt) {
            return Status.Success
        }
        if (process.startedAt && !process.completedAt) {
            return Status.Running
        }
        return Status.Wait
    }

    const handleChangePage = (event: any, page: number) => {
        setPage(page);
    }

    const openRepeatDialog = (process: ProcessInterface) => {
        setSelectedProcess(process)
        setOpen(true)
    }

    let lock = false
    const repeat = async (dialogId: string) => {
        if (lock) {
            return
        }
        if (!selectedProcess) {
            return
        }
        lock = true
        let url = `http://fmw.sipachev.sv/system-monitoring/processes/${selectedProcess.id}/repeat`
        let data = {
            requestId: dialogId
        }
        let response = await fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json;charset=utf-8',
                'X-Plugin-Token': 'passw0rd'
            },
            body: JSON.stringify(data)
        });
        lock = false
        setSelectedProcess(null)
        setOpen(false)
    }

    let formatSize = (length: any) => {
        var i = 0, type = ['b','Kb','Mb','Gb','Tb','Pb'];
        while((length / 1000 | 0) && i < type.length - 1) {
            length /= 1024;
            i++;
        }
        return length.toFixed(2) + ' ' + type[i];
    }

    let remaining = (process: ProcessInterface) =>
    {
        if (!process?.progress?.progress || !process.startedAt) {
            return null
        }
        return Math.round((new Date().getTime() - new Date(process.startedAt).getTime()) / process?.progress.progress * (process?.progress.total - process?.progress.progress));
    }

    let formatTime = ($secs: number | null) =>  {
        if ($secs === null) {
            return ''
        }
        let $timeFormats = [
            [0, '< 1 sec'],
            [1, '1 sec'],
            [2, 'secs', 1],
            [60, '1 min'],
            [120, 'mins', 60],
            [3600, '1 hr'],
            [7200, 'hrs', 3600],
            [86400, '1 day'],
            [172800, 'days', 86400],
        ];

        for (let $index in $timeFormats) {
            let $format = $timeFormats[$index]
            if ($secs >= $format[0]) {
                if ((typeof $timeFormats[$index + 1] !== 'undefined' && $secs < $timeFormats[$index + 1][0])
                    || $index == $timeFormats.length - 1
            ) {
                    if (2 == $format.length) {
                        return $format[1];
                    }

                    return Math.floor($secs / $format[2]) + ' ' + $format[1];
                }
            }
        }
    }

    return (
        <>
            <TableContainer>
                <Table>
                    <TableHead>
                        <TableRow>
                            <TableCell>Name</TableCell>
                            <TableCell>Progress</TableCell>
                            <TableCell>Status</TableCell>
                            <TableCell>Action</TableCell>
                            <TableCell>Created</TableCell>
                        </TableRow>
                    </TableHead>
                    <TableBody>
                        {processes.map((process: ProcessInterface, index: number) => (
                            <TableRow key={process.id}>
                                <TableCell>{process.name}</TableCell>
                                <TableCell>
                                    {!process.progress && !isFinished(process) && <LinearProgress/>}
                                    {!process.progress && isFinished(process) && <LinearProgress variant="determinate" value={100}/>}
                                    {process.progress && <LinearProgress variant="determinate" value={progress(process.progress)}/>}
                                    {process.progress && <span>
                                    {`${process.progress.progress}`} / {`${process.progress.total}`} - {progress(process.progress)}% [{formatSize(process.progress.memory)}] / {formatTime(remaining(process))}
                                </span>}
                                    {canPlay(process) && <IconButton title={`Play`} aria-label="Play">
                                        <PlayCircleOutline/>
                                    </IconButton>}
                                    {canPause(process) && <IconButton title={`Pause`} aria-label="Pause">
                                        <PauseCircleOutline/>
                                    </IconButton>}
                                    {canStop(process) && <IconButton title={`Stop`} aria-label="Stop">
                                        <StopCircleOutlined/>
                                    </IconButton>}
                                </TableCell>
                                <TableCell>
                                    {Status.Error === status(process) && <IconButton title={`Error`} aria-label="Error">
                                        <ErrorOutline/>
                                    </IconButton>}
                                    {Status.Success === status(process) && <IconButton title={`Success`} aria-label="Success">
                                        <CheckCircleOutline/>
                                    </IconButton>}
                                    {Status.Running === status(process) && <IconButton title={`Running`} aria-label="Running">
                                        <RunCircleOutlined/>
                                    </IconButton>}
                                    {Status.Cancelled === status(process) && <IconButton title={`Cancelled`} aria-label="Cancelled">
                                        <StopCircleOutlined/>
                                    </IconButton>}
                                    {Status.Wait === status(process) && <IconButton title={`Wait`} aria-label="Wait">
                                        <HourglassEmptyOutlined/>
                                    </IconButton>}
                                </TableCell>
                                <TableCell>
                                    {canRepeat(process) && <IconButton onClick={() => openRepeatDialog(process)} title={`Repeat`} aria-label="Repeat">
                                        <ReplayOutlined/>
                                    </IconButton>}
                                    {canKill(process) && <IconButton title={`Kill`} aria-label="Kill">
                                        <DeleteForeverOutlined/>
                                    </IconButton>}
                                    {process?.outputId && <IconButton title={`Output`} onClick={() => output(process)} aria-label="Output">
                                        <FactCheckOutlined/>
                                    </IconButton>}
                                </TableCell>
                                <TableCell title={new Date(process.createdAt).toLocaleString()}>
                                    {new Date(process.createdAt).toLocaleDateString()}
                                </TableCell>
                            </TableRow>
                        ))}
                    </TableBody>
                </Table>
            </TableContainer>
            <TablePagination
                component="div"
                count={count}
                rowsPerPage={20}
                page={page}
                onPageChange={handleChangePage}
            />
            <ConfirmDialog open={open} text={'!!!'} agreeCallback={repeat} closeCallback={() => {
                setSelectedProcess(null)
                setOpen(false)
            }}/>
        </>
    )
}
