import {
    Stack,
    Group,
    Card,
    Accordion,
    Text,
    Table,
    RingProgress,
    ScrollArea,
    Space
} from "@mantine/core";
import {marginTopBottom} from "~/styles.ts";
import {PerformanceTestFilter} from "~/components/subs/PerformanceTestFilter.tsx";
import { ResponsiveTableContainer } from "~/components/subs/ResponsiveTableContainer";
import React, {useEffect, useRef, useState} from "react";
import type {BookInfo, PerformanceGetListData} from "~/utils/models.ts";
import { proxy, useSnapshot } from 'valtio';
import {convertNumber, showErrorMessage, sleep} from "~/utils/utils.ts";
import {apiPerformanceGetList, apiPerformanceUploadData} from "~/utils/compare_api.ts";
import Charts from "~/components/Charts.tsx";
import {getTestExampleData, pushTestExampleData} from "~/exapleData.ts";
import {BookTable} from "~/components/subs/BookInfoTable.tsx";
import {confirmExportData} from "~/components/subs/confirms.tsx";

// const performanceDataState = proxy<PerformanceGetListData[]>([]);

export default function Component({isPushTest, performanceDataState}: { isPushTest: boolean, performanceDataState: PerformanceGetListData[] }) {
    const [startsCount, setStartsCount] = useState(200)
    const [endsCount, setEndsCount] = useState(100000)
    const [addCount, setAddCount] = useState(200)
    const [avgCount, setAvgCount] = useState(3)
    const [maxExampleCount, setMaxExampleCount] = useState(30)

    const [isStarting, setIsStarting] = useState(false)
    const [requestStopping, setRequestStopping] = useState(false)
    const requestStoppingRef = useRef(requestStopping);
    requestStoppingRef.current = requestStopping;

    const [currentCount, setCurrentCount] = useState(0)
    const [currentIdx, setCurrentIdx] = useState(0)
    const [currentAvgCount, setCurrentAvgCount] = useState(0)

    const [hideServerDeserialize, setHideServerDeserialize] = useState(!isPushTest)
    const [hideServerSerialize, setHideServerSerialize] = useState(isPushTest)
    const [chartsSize, setChartsSize] = useState(6)

    const performanceDataSnap = useSnapshot(performanceDataState);

    useEffect(() => {
        getExampleData()
    }, [])

    const exportData = () => {
        if (!performanceDataSnap) return
        confirmExportData(performanceDataSnap as PerformanceGetListData[], isPushTest ? "performance_test_push_data" : "performance_test_get_list_data")
    }

    const updateFilter = (startsCount: number, endsCount: number, addCount: number, avgCount: number, maxExampleCount: number, hideServerDeserialize: boolean, hideServerSerialize: boolean, chartsSize: number) => {
        setStartsCount(startsCount);
        setEndsCount(endsCount);
        setAddCount(addCount);
        setAvgCount(avgCount);
        setMaxExampleCount(maxExampleCount);
        setHideServerDeserialize(hideServerDeserialize);
        setHideServerSerialize(hideServerSerialize);
        setChartsSize(chartsSize);
    }

    const onClickStart = () => {
        StartTest()
            .finally(() => setIsStarting(false))
    }

    const OnClickStop = () => {
        if (!isStarting) return
        setRequestStopping(true)
    }

    const getCurrentSingleStep = (queryIdx: number | undefined) => {
        if (!isStarting) return queryIdx;
        if (queryIdx !== currentIdx) return queryIdx;
        let value = currentAvgCount / avgCount * 100;
        return <RingProgress
            sections={[{ value, color: 'blue' }]}
            transitionDuration={250}
            label={<Text ta="center">{currentAvgCount} / {avgCount}</Text>}
        />

    }

    const getExampleData = () => {
        performanceDataState.splice(0);
        let exampleData = isPushTest ? pushTestExampleData : getTestExampleData
        for (let i = 0; i < exampleData.length; i++) {
            // @ts-ignore
            performanceDataState.push(exampleData[i])
        }
    }

    const StartTest = async () => {
        setIsStarting(true)
        setRequestStopping(false)

        if (addCount <= 0) {
            showErrorMessage("Step Count must be greater than 0", "Invalid Step Count");
            return;
        }

        if (startsCount > endsCount) {
            showErrorMessage("Starts Count can not be greater than Ends Count", "Invalid Starts Count");
            return;
        }

        let currentForCount = startsCount;
        let idx = 0;
        performanceDataState.splice(0);

        while (true) {
            setCurrentCount(currentForCount);
            setCurrentIdx(idx)

            for (let i = 0; i < avgCount; i++) {
                try {
                    if (requestStoppingRef.current) {
                        setRequestStopping(false)
                        return
                    }

                    let response = isPushTest ?
                        await apiPerformanceUploadData(currentForCount, idx) :
                        await apiPerformanceGetList(currentForCount, maxExampleCount, idx)

                    if (response.success && response.data) {
                        const checkLastIndex = performanceDataState.length - 1
                        if (performanceDataState.length > 0 && performanceDataState[checkLastIndex].idx === response.data.idx) {
                            const lastData = performanceDataState[checkLastIndex]
                            performanceDataState[checkLastIndex].grpc.client_networking_ping = (lastData.grpc.client_networking_ping + response.data.grpc.client_networking_ping) / 2;
                            performanceDataState[checkLastIndex].grpc.client_request_cpu = (lastData.grpc.client_request_cpu + response.data.grpc.client_request_cpu) / 2;
                            performanceDataState[checkLastIndex].grpc.client_request_time = (lastData.grpc.client_request_time + response.data.grpc.client_request_time) / 2;
                            performanceDataState[checkLastIndex].grpc.networking_size.request_size = (lastData.grpc.networking_size.request_size + response.data.grpc.networking_size.request_size) / 2;
                            performanceDataState[checkLastIndex].grpc.networking_size.response_size = (lastData.grpc.networking_size.response_size + response.data.grpc.networking_size.response_size) / 2;
                            performanceDataState[checkLastIndex].grpc.server_deserialize_cpu = (lastData.grpc.server_deserialize_cpu + response.data.grpc.server_deserialize_cpu) / 2;
                            performanceDataState[checkLastIndex].grpc.server_deserialize_time = (lastData.grpc.server_deserialize_time + response.data.grpc.server_deserialize_time) / 2;
                            performanceDataState[checkLastIndex].grpc.server_serialize_cpu = (lastData.grpc.server_serialize_cpu + response.data.grpc.server_serialize_cpu) / 2;
                            performanceDataState[checkLastIndex].grpc.server_serialize_time = (lastData.grpc.server_serialize_time + response.data.grpc.server_serialize_time) / 2;
                            performanceDataState[checkLastIndex].grpc.server_protocol_total_time = (lastData.grpc.server_protocol_total_time + response.data.grpc.server_protocol_total_time) / 2;

                            performanceDataState[checkLastIndex].rest.client_networking_ping = (lastData.rest.client_networking_ping + response.data.rest.client_networking_ping) / 2;
                            performanceDataState[checkLastIndex].rest.client_request_cpu = (lastData.rest.client_request_cpu + response.data.rest.client_request_cpu) / 2;
                            performanceDataState[checkLastIndex].rest.client_request_time = (lastData.rest.client_request_time + response.data.rest.client_request_time) / 2;
                            performanceDataState[checkLastIndex].rest.networking_size.request_size = (lastData.rest.networking_size.request_size + response.data.rest.networking_size.request_size) / 2;
                            performanceDataState[checkLastIndex].rest.networking_size.response_size = (lastData.rest.networking_size.response_size + response.data.rest.networking_size.response_size) / 2;
                            performanceDataState[checkLastIndex].rest.server_deserialize_cpu = (lastData.rest.server_deserialize_cpu + response.data.rest.server_deserialize_cpu) / 2;
                            performanceDataState[checkLastIndex].rest.server_deserialize_time = (lastData.rest.server_deserialize_time + response.data.rest.server_deserialize_time) / 2;
                            performanceDataState[checkLastIndex].rest.server_serialize_cpu = (lastData.rest.server_serialize_cpu + response.data.rest.server_serialize_cpu) / 2;
                            performanceDataState[checkLastIndex].rest.server_serialize_time = (lastData.rest.server_serialize_time + response.data.rest.server_serialize_time) / 2;
                            performanceDataState[checkLastIndex].rest.server_protocol_total_time = (lastData.rest.server_protocol_total_time + response.data.rest.server_protocol_total_time) / 2;
                        }
                        else {
                            performanceDataState.push(response.data)
                        }
                    }
                }
                catch (error: any) {
                    showErrorMessage(error.toString(), "Error during performance test");
                }
                finally {
                    setCurrentAvgCount(i + 1)
                }
            }
            idx++;
            setCurrentIdx(idx)
            await sleep(200)

            if (currentForCount >= endsCount) {
                break;
            }

            currentForCount += addCount;
            if (currentForCount > endsCount) {
                currentForCount = endsCount;
            }
        }
    }

    const rows = performanceDataSnap.map(item => (
        <Table.Tr key={`${item.idx}_${item.request_count}`}>
            <Table.Td>{getCurrentSingleStep(item.idx)}</Table.Td>
            <Table.Td>{item.request_count}</Table.Td>
            <Table.Td>{convertNumber(item.grpc.client_networking_ping, 1000, 2)} / {convertNumber(item.rest.client_networking_ping, 1000, 2)}</Table.Td>
            <Table.Td>{convertNumber(item.grpc.client_request_cpu, 1, 2)} / {convertNumber(item.rest.client_request_cpu, 1, 2)}</Table.Td>
            <Table.Td>{convertNumber(item.grpc.client_request_time, 1, 2)} / {convertNumber(item.rest.client_request_time, 1, 2)}</Table.Td>
            <Table.Td>{convertNumber(item.grpc.server_deserialize_cpu, 1, 2)} / {convertNumber(item.rest.server_deserialize_cpu, 1, 2)}</Table.Td>
            <Table.Td>{convertNumber(item.grpc.server_deserialize_time, 1000, 4)} / {convertNumber(item.rest.server_deserialize_time, 1000, 4)}</Table.Td>
            <Table.Td>{convertNumber(item.grpc.server_serialize_cpu, 1, 2)} / {convertNumber(item.rest.server_serialize_cpu, 1, 2)}</Table.Td>
            <Table.Td>{convertNumber(item.grpc.server_serialize_time, 1000, 4)} / {convertNumber(item.rest.server_serialize_time, 1000, 4)}</Table.Td>
            <Table.Td>{convertNumber(item.grpc.networking_size.request_size, 1 / 1024, 4)} / {convertNumber(item.rest.networking_size.request_size, 1 / 1024, 4)}</Table.Td>
            <Table.Td>{convertNumber(item.grpc.networking_size.response_size, 1 / 1024, 4)} / {convertNumber(item.rest.networking_size.response_size, 1 / 1024, 4)}</Table.Td>
        </Table.Tr>
    ))

    return (
        <Stack>
            <Card padding="lg" radius="md" withBorder style={marginTopBottom}>

                <Card.Section withBorder>
                    <Accordion variant="filled" chevronPosition="left" defaultValue="advanced-filter">
                        <Accordion.Item value="advanced-filter">
                            <Accordion.Control>
                                <Group>
                                    <Text>Config</Text>
                                </Group>
                            </Accordion.Control>
                            <Accordion.Panel>
                                <PerformanceTestFilter onChange={updateFilter} onClickStart={onClickStart} onClickStop={OnClickStop} onClickExportData={exportData}
                                                       isStarting={isStarting} currentCount={currentCount} requestStopping={requestStopping} isPushTest={isPushTest} />
                            </Accordion.Panel>
                        </Accordion.Item>
                    </Accordion>
                </Card.Section>

                <Card.Section withBorder>
                    <Charts data={performanceDataSnap.flatMap(item => item || []) as PerformanceGetListData[]}
                            gridSpan={chartsSize} hideServerDeserialize={hideServerDeserialize} hideServerSerialize={hideServerSerialize} />
                </Card.Section>

                <Card.Section>
                    <ResponsiveTableContainer minWidth={600}>
                        <Table striped highlightOnHover withColumnBorders withTableBorder>
                            <Table.Thead>
                                <Table.Tr>
                                    <Table.Th colSpan={2}></Table.Th>
                                    <Table.Th colSpan={3}>Client (gRPC / Rest)</Table.Th>
                                    <Table.Th colSpan={2}>Server Deserialization (gRPC / Rest)</Table.Th>
                                    <Table.Th colSpan={2}>Server Serialization (gRPC / Rest)</Table.Th>
                                    <Table.Th colSpan={2}>Networking Pack Size (gRPC / Rest)</Table.Th>
                                </Table.Tr>
                                <Table.Tr key="header">
                                    <Table.Th>Index</Table.Th>
                                    <Table.Th>Count</Table.Th>
                                    <Table.Th>Ping (ms)</Table.Th>
                                    <Table.Th>CPU (%)</Table.Th>
                                    <Table.Th>Time (s)</Table.Th>
                                    <Table.Th>CPU (%)</Table.Th>
                                    <Table.Th>Time (ms)</Table.Th>
                                    <Table.Th>CPU (%)</Table.Th>
                                    <Table.Th>Time (ms)</Table.Th>
                                    <Table.Th>Request (KB)</Table.Th>
                                    <Table.Th>Response (KB)</Table.Th>
                                </Table.Tr>
                            </Table.Thead>
                            <Table.Tbody>
                                {rows}
                            </Table.Tbody>
                        </Table>
                    </ResponsiveTableContainer>

                    {/*<Pagination withEdges total={staffInfo.total_pages} value={currPage} onChange={setCurrPage} mt="sm"*/}
                    {/*            style={{justifyItems: "flex-end", ...marginRightBottom}}/>*/}
                </Card.Section>

                {!isPushTest &&
                    <Card.Section>
                        <Space h="lg"/>
                        <Text ta="center" fw={700}>Example Books Data</Text>
                        <ScrollArea w="100%">
                            <BookTable books={(performanceDataSnap && performanceDataSnap.length > 0 ? performanceDataSnap[performanceDataSnap.length - 1].rest.response_data || [] : []) as BookInfo[]} />
                        </ScrollArea>
                    </Card.Section>
                }
            </Card>
        </Stack>
    )
}

