/*
 * -------------------------------------------------------------------------
 * 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 React, { useEffect, useMemo, useState } from 'react';
import i18n from '@insight/lib/i18n';
import { useTranslation } from 'react-i18next';
import type { TFunction } from 'i18next';
import { runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { Tag } from 'antd';
import { ResizeTable, fetchColumnFilterProps } from '@insight/lib/resize';
import { Tooltip } from '@insight/lib/components';
import { Session } from '../entity/session';
import { getBlockTableData } from './dataHandler';
import { generateJsonShow } from '../utils/utils';

const DEFAULT_TABLE_HEIGHT = 400;
const TABLE_CHROME_HEIGHT = 88;
const MIN_TABLE_SCROLL_Y = 120;

const getRecordValue = (record: any, keys: string[]): any => {
    for (const key of keys) {
        if (record?.[key] !== undefined && record?.[key] !== null && record?.[key] !== '') {
            return record[key];
        }
    }
    return undefined;
};

const toNumber = (value: any): number | null => {
    const num = Number(value);
    return Number.isNaN(num) ? null : num;
};

const isValidBlockId = (value: number | null): boolean => value !== null;
const isValidEventId = (value: number | null): boolean => value !== null && value >= 0;
const isBlockIdColumn = (col: any): boolean => ['id', 'ID', 'blockId', 'Block ID'].includes(String(col.key ?? col.name));
const isAllocEventIdColumn = (col: any): boolean => ['Alloc Event ID', 'allocEventId', 'allocOrMapEventId'].includes(String(col.key ?? col.name));
const isFreeEventIdColumn = (col: any): boolean => ['Free Event ID', 'freeEventId', 'freeOrUnmapEventId'].includes(String(col.key ?? col.name));

const LocateLink = ({ disabled, children, onClick }: { disabled: boolean; children: React.ReactNode; onClick: () => void }): React.ReactElement => (
    <button
        type="button"
        disabled={disabled}
        onClick={(event): void => {
            event.stopPropagation();
            if (!disabled) {
                onClick();
            }
        }}
        onMouseEnter={(event): void => {
            if (!disabled) {
                event.currentTarget.style.textDecoration = 'underline';
            }
        }}
        onMouseLeave={(event): void => {
            event.currentTarget.style.textDecoration = 'none';
        }}
        style={{
            border: 0,
            padding: 0,
            color: disabled ? 'inherit' : '#1677ff',
            background: 'transparent',
            cursor: disabled ? 'default' : 'pointer',
            textAlign: 'left',
            font: 'inherit',
        }}
        className="locate-link"
    >
        {children}
    </button>
);

const columnRender = (col: any, text: string, record: any, t: TFunction, session: Session): React.ReactNode => {
    const tags: { [key: string]: boolean } = { 'early-alloc': record.lazyUsed, 'late-free': record.delayedFree, idle: record.longIdle };
    const showTag = Object.keys(tags).filter(tag => tags[tag]);
    const blockId = toNumber(getRecordValue(record, ['id', 'ID', 'blockId', 'Block ID']));
    const allocEventId = toNumber(getRecordValue(record, ['Alloc Event ID', 'allocEventId', 'allocOrMapEventId']));
    const freeEventId = toNumber(getRecordValue(record, ['Free Event ID', 'freeEventId', 'freeOrUnmapEventId']));
    const locateBlock = (): void => {
        if (!isValidBlockId(blockId)) return;
        runInAction(() => {
            session.pendingBlockLocateId = blockId as number;
        });
    };
    const locateEvent = (eventId: number | null): void => {
        if (!isValidEventId(eventId)) return;
        runInAction(() => {
            session.pendingEventLocate = { eventId: eventId as number, deviceId: session.deviceId };
        });
    };
    if (isBlockIdColumn(col)) {
        return <>
            <LocateLink disabled={!isValidBlockId(blockId)} onClick={locateBlock}>
                <span style={{ marginRight: '5px' }}>{text}</span>
            </LocateLink>
            {showTag.map((tag) => <Tag key={tag} color="red">{t(tag)}</Tag>)}
        </>;
    }
    if (session.module !== 'leaks' && isAllocEventIdColumn(col)) {
        return <LocateLink disabled={!isValidEventId(allocEventId)} onClick={(): void => locateEvent(allocEventId)}>{text ?? ''}</LocateLink>;
    }
    if (session.module !== 'leaks' && isFreeEventIdColumn(col)) {
        return <LocateLink disabled={!isValidEventId(freeEventId)} onClick={(): void => locateEvent(freeEventId)}>{text ?? ''}</LocateLink>;
    }
    return <Tooltip
        title={col.key === 'attr' && text ? generateJsonShow(text) : text || ''}
        placement="top"
    >
        {text ?? ''}
    </Tooltip>;
};
const getTableColumns = (t: TFunction, session: Session): any => {
    return session.blocksTableHeader.map((col: any) => {
        const item = {
            dataIndex: col.key,
            key: col.key,
            title: isBlockIdColumn(col) ? t('blockId', { keyPrefix: 'tableHead' }) : t(col.name, { defaultValue: col.name, keyPrefix: 'tableHead' }),
            sorter: col.sortable,
            ellipsis: {
                showTitle: false,
            },
            showSorterTooltip: t(col.name, { keyPrefix: 'tableHeadTooltip', defaultValue: '' }) === ''
                ? true
                : { title: t(col.name, { keyPrefix: 'tableHeadTooltip' }) },
            render: (text: string, record: any): React.ReactNode => columnRender(col, text, record, t, session),
        };
        if (col.searchable) {
            return { ...item, ...fetchColumnFilterProps(col.key, col.name.replace(' ', '')) };
        } else if (col.rangeFilterable) {
            const filterOptions = { min: col.min, max: col.max };
            return { ...item, ...fetchColumnFilterProps(col.key, col.name.replace(' ', ''), true, filterOptions) };
        } else {
            return item;
        }
    });
};
const handleFilters = (filters: any, session: Session): void => {
    const newFilters: { [key: string]: string } = {};
    const newRangeFilters: { [key: string]: number[] } = {};
    const oldFilters = Object.keys(filters);
    oldFilters.forEach((key: string) => {
        const isRange = filters[key]?.length === 2;
        if (isRange) {
            newRangeFilters[key] = filters[key].map(Number);
        } else {
            if (filters[key]?.[0] !== undefined) { newFilters[key] = filters[key]?.[0]; }
        }
    });
    runInAction(() => {
        session.blocksFilters = newFilters;
        session.blocksRangeFilters = newRangeFilters;
    });
};
const BlocksTable = observer(({ session, height }: { session: Session; height?: number }): React.ReactElement => {
    const { t } = useTranslation('leaks');
    const {
        deviceId, eventType, blocksTableData, blocksTableHeader, tableKey,
        blocksCurrentPage, blocksPageSize, blocksTotal, blocksOrder, blocksOrderBy,
        blocksFilters, blocksRangeFilters, maxTime, minTime,
        lazyUsedThreshold, delayedFreeThreshold, longIdleThreshold, onlyInefficient,
        autoFilterPotentialLeaks,
    } = session;
    const [loading, setLoading] = useState(false);
    const defaultDataSource = (process.env.NODE_ENV === 'development' ? [{}] : []);
    const columns = useMemo(() => getTableColumns(t, session), [JSON.stringify(blocksTableHeader), session.module, t]);
    const scrollX = Math.max(120 * columns.length, 1000);
    const tableHeight = Math.max(height ?? DEFAULT_TABLE_HEIGHT, MIN_TABLE_SCROLL_Y + TABLE_CHROME_HEIGHT);
    const scrollY = Math.max(MIN_TABLE_SCROLL_Y, tableHeight - TABLE_CHROME_HEIGHT);
    const onTableChange = (pagination: any, filters: any, sorter: any, extra: any): void => {
        if (extra.action === 'sort') {
            runInAction(() => {
                if (sorter.order === undefined) {
                    session.blocksOrder = '';
                } else {
                    session.blocksOrder = sorter.order !== 'ascend';
                }
                session.blocksOrderBy = sorter.field;
            });
        }
        if (extra.action === 'filter') {
            handleFilters(filters, session);
        }
    };
    const onChange = (newCurrent: number, newPageSize: number): void => {
        runInAction(() => {
            session.blocksCurrentPage = newCurrent;
            session.blocksPageSize = newPageSize;
        });
    };
    useEffect(() => {
        if (deviceId === '' || maxTime === 0 || maxTime === undefined) return;
        setLoading(true);
        getBlockTableData(session);
        setLoading(false);
    }, [deviceId, eventType, maxTime, minTime, blocksCurrentPage,
        blocksPageSize, blocksOrder, blocksOrderBy, JSON.stringify(blocksFilters), JSON.stringify(blocksRangeFilters),
        JSON.stringify(lazyUsedThreshold), JSON.stringify(delayedFreeThreshold), JSON.stringify(longIdleThreshold),
        onlyInefficient, autoFilterPotentialLeaks,
    ]);
    return (
        <>
            <ResizeTable
                data-testid={'blocksTable'}
                columns={columns}
                dataSource={blocksTableData.length === 0 ? defaultDataSource : blocksTableData.map((item: any, index: number) => ({ ...item, key: `${item.id}_${index}` }))}
                rowKey={(record: any, index?: number): string => `${record.id ?? record.ID ?? 'block'}_${index ?? 0}`}
                onChange={onTableChange}
                pagination={{
                    current: blocksCurrentPage,
                    pageSize: blocksPageSize,
                    pageSizeOptions: [10, 20, 30, 50, 100],
                    onChange,
                    total: blocksTotal,
                    showTotal: (totalNum: number): string => i18n.t('PaginationTotal', { total: totalNum }),
                    showQuickJumper: true,
                }}
                scroll={{ x: scrollX, y: scrollY }}
                style={{ height: tableHeight }}
                loading={loading}
                key={`${tableKey}_BLocks`}
            />
        </>
    );
});

export default BlocksTable;