/*
 * -------------------------------------------------------------------------
 * 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 { ResizeTable, fetchColumnFilterProps } from '@insight/lib/resize';
import { Tooltip } from '@insight/lib/components';
import { Session } from '../entity/session';
import { getEventTableData } 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 isValidLocateId = (value: number | null): boolean => value !== null && value >= 0;
const isEventIdColumn = (col: any): boolean => ['id', 'ID', 'eventId', 'Event ID'].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 isCallStackColumn = (col: any): boolean => {
    const key = String(col.key ?? '').toLowerCase();
    const name = String(col.name ?? '').toLowerCase();
    return key.includes('callstack') || key.includes('call stack') || name.includes('callstack') || name.includes('call stack');
};

const getTooltipTitle = (col: any, text: string): React.ReactNode => {
    const title = col.key === 'attr' && text ? generateJsonShow(text) : text ?? '';
    if (!isCallStackColumn(col)) {
        return title;
    }
    return <div style={{ maxHeight: 280, overflow: 'auto', maxWidth: 560 }}>{title}</div>;
};

const getTableColumns = (t: TFunction, session: Session): any => {
    const columns = session.eventsTableHeader.map((col: any) => {
        const item = {
            dataIndex: col.key,
            key: col.key,
            title: 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 => {
                if (session.module !== 'leaks' && isEventIdColumn(col)) {
                    const eventId = toNumber(getRecordValue(record, ['id', 'ID', 'eventId', 'Event ID']));
                    return <LocateLink
                        disabled={!isValidLocateId(eventId)}
                        onClick={(): void => {
                            if (!isValidLocateId(eventId)) return;
                            runInAction(() => {
                                session.pendingEventLocate = { eventId: eventId as number, deviceId: session.deviceId };
                            });
                        }}
                    >
                        {text ?? ''}
                    </LocateLink>;
                }
                return <Tooltip
                    title={getTooltipTitle(col, text)}
                    placement="top"
                    overlayInnerStyle={isCallStackColumn(col) ? { maxHeight: 300, overflow: 'hidden', maxWidth: 600 } : undefined}
                >
                    {text ?? ''}
                </Tooltip>;
            },
        };
        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;
        }
    });
    return columns;
};
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.eventsFilters = newFilters;
        session.eventsRangeFilters = newRangeFilters;
    });
};
const EventsTable = observer(({ session, height }: { session: Session; height?: number }): React.ReactElement => {
    const { t } = useTranslation('leaks');
    const {
        deviceId, eventsTableData, eventsTableHeader, eventsCurrentPage, tableKey,
        eventsPageSize, eventsTotal, eventsOrder, eventsOrderBy, eventsFilters,
        eventsRangeFilters, maxTime, minTime,
    } = session;
    const [loading, setLoading] = useState(false);
    const defaultDataSource = (process.env.NODE_ENV === 'development' ? [{}] : []);
    const columns = useMemo(() => getTableColumns(t, session), [JSON.stringify(eventsTableHeader), session.module, t]);
    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.eventsOrder = '';
                } else {
                    session.eventsOrder = sorter.order !== 'ascend';
                }
                session.eventsOrderBy = sorter.field;
            });
        }
        if (extra.action === 'filter') {
            handleFilters(filters, session);
        }
    };
    const onChange = (newCurrent: number, newPageSize: number): void => {
        runInAction(() => {
            session.eventsCurrentPage = newCurrent;
            session.eventsPageSize = newPageSize;
        });
    };
    useEffect(() => {
        if (deviceId === '' || maxTime === 0 || maxTime === undefined) return;
        setLoading(true);
        getEventTableData(session);
        setLoading(false);
    }, [deviceId, maxTime, minTime, eventsCurrentPage, eventsPageSize, eventsOrder, eventsOrderBy, JSON.stringify(eventsFilters), JSON.stringify(eventsRangeFilters)]);
    return (
        <>
            <ResizeTable
                data-testid={'eventsTable'}
                columns={columns}
                dataSource={eventsTableData.length === 0 ? defaultDataSource : eventsTableData.map((item: any, index: number) => ({ ...item, key: `${item.id}_${index}` }))}
                rowKey={(record: any, index?: number): string => `${record.id ?? record.ID ?? 'event'}_${index ?? 0}`}
                onChange={onTableChange}
                pagination={{
                    current: eventsCurrentPage,
                    pageSize: eventsPageSize,
                    pageSizeOptions: [10, 20, 30, 50, 100],
                    onChange,
                    total: eventsTotal,
                    showTotal: (totalNum: number): string => i18n.t('PaginationTotal', { total: totalNum }),
                    showQuickJumper: true,
                }}
                scroll={{ x: 150 * columns.length, y: scrollY }}
                style={{ height: tableHeight }}
                loading={loading}
                key={`${tableKey}_Events`}
            />
        </>
    );
});

export default EventsTable;