/*
 * -------------------------------------------------------------------------
 * This file is part of the MindStudio project.
 * Copyright (c) 2025 Huawei Technologies Co.,Ltd.
 *
 * MindStudio is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *          http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * -------------------------------------------------------------------------
 */
import { workerSetMemoryBlockData, workerTransform } from '@/leaksWorker/blockWorker/worker';
import {
    getMemoryDetailData, getFuncData, getBlockDetails, getEventDetails,
    FuncParam, type BlockParam, EventParam,
    ThreShold,
    getBlocksGraphData,
    getLeaksAllocationsData,
    getSnapshotBlocks,
    getSnapshotBlockTable,
    getSnapshotEvent,
    getSnapshotAllocations,
    Allocation,
    getSnapshotLeakStats,
} from '../utils/RequestUtils';
import { message } from 'antd';
import { runInAction } from 'mobx';

const funcDataRequestSeqMap = new WeakMap<object, number>();

export const getFuncNewData = async (
    session: any,
    startTimestamp?: number,
    endTimestamp?: number,
    shouldApply: () => boolean = () => true,
): Promise<void> => {
    const requestSeq = (funcDataRequestSeqMap.get(session) ?? 0) + 1;
    funcDataRequestSeqMap.set(session, requestSeq);
    const isLatestRequest = (): boolean => funcDataRequestSeqMap.get(session) === requestSeq && shouldApply();

    runInAction(() => {
        session.loadingFunc = true;
    });
    try {
        const funcParam: FuncParam = { deviceId: session.deviceId, relativeTime: true, threadId: session.threadId, allowTrim: session.allowTrim };
        if (startTimestamp !== undefined && endTimestamp !== undefined) {
            funcParam.startTimestamp = startTimestamp;
            funcParam.endTimestamp = endTimestamp;
        }
        const funcData = await getFuncData(funcParam);
        if (!isLatestRequest()) {
            return;
        }

        runInAction(() => {
            session.funcData = funcData;
            session.funcOptions = [...new Set(funcData.traces.map(trace => trace.func))].map(func => ({ label: func, value: func }));
            const funcSet = new Set(session.searchFunc);
            session.searchFunc = funcSet.size ? session.funcOptions.filter((item: any) => funcSet.has(item.value)).map((i: any) => i.value) : [];
            if (startTimestamp !== undefined && endTimestamp !== undefined) {
                session.maxTime = endTimestamp;
                session.minTime = startTimestamp;
            } else {
                session.maxTime = funcData.maxTimestamp;
                session.minTime = funcData.minTimestamp;
            }
            session.maxDepth = funcData.maxDepth;
            session.loadingFunc = false;
        });
    } catch (error: any) {
        if (!isLatestRequest()) {
            return;
        }
        runInAction(() => {
            session.loadingFunc = false;
        });
        message.error(error.message);
    }
};
export const getBarNewData = async (session: any, startTimestamp?: number, endTimestamp?: number): Promise<void> => {
    const getBlocksRequest = session.module === 'leaks' ? getBlocksGraphData : getSnapshotBlocks;
    const getAllocationRequest = session.module === 'leaks' ? getLeaksAllocationsData : getSnapshotAllocations;
    runInAction(() => {
        session.loadingBlocks = true;
    });
    try {
        const param: BlockParam = { deviceId: session.deviceId, relativeTime: true, eventType: session.eventType, isTable: false };
        const blockData = await getBlocksRequest(param);
        const transform = { x: 0, y: 0, scaleX: 1, scaleY: 1 };
        runInAction(() => {
            session.leaksWorkerInfo.renderOptions.transform = transform;
        });
        workerTransform({ transform });
        const allocationData = await getAllocationRequest(param);
        if (session.module === 'memsnapshot') {
            const reservedLine = allocationData.allocations
                .filter((item): item is Allocation & { reservedSize: number } => typeof item.reservedSize === 'number')
                .map(item => [item.timestamp, item.reservedSize] as [number, number]);
            blockData.reservedLine = reservedLine;
            blockData.reservedSizeMax = reservedLine.reduce((max, [, reservedSize]) => Math.max(max, reservedSize), blockData.maxSize);
        }
        workerSetMemoryBlockData({ data: blockData });
        runInAction(() => {
            session.allocationData = allocationData;
        });
    } catch (error: any) {
        message.error(error.message);
    }
};
export const getNewDetailData = async (session: any): Promise<void> => {
    try {
        const memoryDatas = await getMemoryDetailData(session.deviceId, session.memoryStamp, session.eventType);
        runInAction(() => {
            session.memoryData = memoryDatas;
        });
    } catch (error: any) {
        message.error(error.message);
    }
};
const handleThreshold = (blockParam: any, session: any): void => {
    const { lazyUsedThreshold, delayedFreeThreshold, longIdleThreshold } = session;
    if (lazyUsedThreshold.valueT === null && lazyUsedThreshold.perT === null && delayedFreeThreshold.valueT === null &&
        delayedFreeThreshold.perT === null && longIdleThreshold.valueT === null && longIdleThreshold.perT === null
    ) return;
    const threshold: { [key: string]: ThreShold } = {};
    if (lazyUsedThreshold.valueT !== null || lazyUsedThreshold.perT !== null) {
        blockParam.lazyUsedThreshold = { perT: null, valueT: null };
        threshold.lazyUsedThreshold = lazyUsedThreshold;
    }
    if (delayedFreeThreshold.valueT !== null || delayedFreeThreshold.perT !== null) {
        blockParam.delayedFreeThreshold = { perT: null, valueT: null };
        threshold.delayedFreeThreshold = delayedFreeThreshold;
    }
    if (longIdleThreshold.valueT !== null || longIdleThreshold.perT !== null) {
        blockParam.longIdleThreshold = { perT: null, valueT: null };
        threshold.longIdleThreshold = longIdleThreshold;
    }
    Object.keys(threshold).forEach((key) => {
        if (threshold[key].valueT !== null) {
            blockParam[key].valueT = threshold[key].valueT;
        } else {
            blockParam[key].valueT = 0;
        }
        if (threshold[key].perT !== null) {
            blockParam[key].perT = threshold[key].perT;
        } else {
            blockParam[key].perT = 0;
        }
    });
};
export const getBlockTableData = async (session: any): Promise<void> => {
    const request = session.module === 'leaks' ? getBlockDetails : getSnapshotBlockTable;
    try {
        const blockParam: BlockParam = {
            deviceId: session.deviceId,
            relativeTime: true,
            eventType: session.eventType,
            isTable: true,
            startTimestamp: session.minTime,
            endTimestamp: session.maxTime,
            currentPage: session.blocksCurrentPage,
            pageSize: session.blocksPageSize,
        };
        if (session.blocksOrder !== '') {
            blockParam.desc = session.blocksOrder;
            blockParam.orderBy = session.blocksOrderBy;
        }
        if (Object.keys(session.blocksFilters).length > 0) {
            blockParam.filters = session.blocksFilters;
        }
        if (Object.keys(session.blocksRangeFilters).length > 0) {
            blockParam.rangeFilters = session.blocksRangeFilters;
        }
        if (session.onlyInefficient) {
            blockParam.onlyInefficient = true;
        }
        if (session.autoFilterPotentialLeaks) {
            blockParam.onlyUnreleasedInRange = true;
        }
        handleThreshold(blockParam, session);
        const blockTableData = await request(blockParam);
        runInAction(() => {
            session.blocksTableData = blockTableData.blocks;
            session.blocksTableHeader = blockTableData.headers;
            session.blocksTotal = blockTableData.total;
        });
    } catch (error: any) {
        message.error(error.message);
    }
};
export const getPotentialLeakStats = async (session: any, range?: [number, number]): Promise<void> => {
    const startTimestamp = range?.[0] ?? session.minTime;
    const endTimestamp = range?.[1] ?? session.maxTime;
    if (session.module !== 'memsnapshot' || session.deviceId === '' || endTimestamp === 0 || endTimestamp === undefined) return;
    const requestId = session.leakStats.requestId + 1;
    runInAction(() => {
        session.leakStats.loading = true;
        session.leakStats.error = false;
        session.leakStats.requestId = requestId;
    });
    try {
        const leakStats = await getSnapshotLeakStats({
            deviceId: session.deviceId,
            startTimestamp,
            endTimestamp,
        });
        runInAction(() => {
            if (session.leakStats.requestId !== requestId) return;
            session.leakStats = { ...leakStats, loading: false, error: false, requestId };
        });
    } catch (error: any) {
        runInAction(() => {
            if (session.leakStats.requestId !== requestId) return;
            session.leakStats.loading = false;
            session.leakStats.error = true;
        });
        message.error(error.message);
    }
};
export const getEventTableData = async (session: any): Promise<void> => {
    const request = session.module === 'leaks' ? getEventDetails : getSnapshotEvent;
    try {
        const eventParam: EventParam = {
            deviceId: session.deviceId,
            relativeTime: true,
            startTimestamp: session.minTime,
            endTimestamp: session.maxTime,
            currentPage: session.eventsCurrentPage,
            pageSize: session.eventsPageSize,
            isTable: true,
        };
        if (session.eventsOrder !== '') {
            eventParam.desc = session.eventsOrder;
            eventParam.orderBy = session.eventsOrderBy;
        }
        if (Object.keys(session.eventsFilters).length > 0) {
            eventParam.filters = session.eventsFilters;
        }
        if (Object.keys(session.eventsRangeFilters).length > 0) {
            eventParam.rangeFilters = session.eventsRangeFilters;
        }
        const eventTableData = await request(eventParam);
        runInAction(() => {
            session.eventsTableData = eventTableData.events;
            session.eventsTableHeader = eventTableData.headers;
            session.eventsTotal = eventTableData.total;
        });
    } catch (error: any) {
        message.error(error.message);
    }
};