import React, { useCallback, useMemo, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { Color } from '@signozhq/design-tokens';
import { Button, Popover, Spin, Tooltip, Tree } from 'antd';
import type { DataNode } from 'antd/es/tree';
import GroupByIcon from 'assets/CustomIcons/GroupByIcon';
import cx from 'classnames';
import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import { QueryParams } from 'constants/query';
import { OPERATORS } from 'constants/queryBuilder';
import ROUTES from 'constants/routes';
import { ChangeViewFunctionType } from 'container/ExplorerOptions/types';
import { RESTRICTED_SELECTED_FIELDS } from 'container/LogsFilters/config';
import { MetricsType } from 'container/MetricsApplication/constant';
import { useGetSearchQueryParam } from 'hooks/queryBuilder/useGetSearchQueryParam';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { ICurrentQueryData } from 'hooks/useHandleExplorerTabChange';
import {
	ArrowDownToDot,
	ArrowUpFromDot,
	Ellipsis,
	RefreshCw,
} from 'lucide-react';
import { ExplorerViews } from 'pages/LogsExplorer/utils';
import { useTimezone } from 'providers/Timezone';
import {
	BaseAutocompleteData,
	DataTypes,
} from 'types/api/queryBuilder/queryAutocompleteResponse';

import { DataType } from '../TableView';
import {
	filterKeyForField,
	getFieldAttributes,
	getSanitizedLogBody,
	parseFieldValue,
	removeEscapeCharacters,
} from '../utils';
import useAsyncJSONProcessing from './useAsyncJSONProcessing';

import './TableViewActions.styles.scss';

interface ITableViewActionsProps {
	fieldData: Record<string, string>;
	record: DataType;
	isListViewPanel: boolean;
	isfilterInLoading: boolean;
	isfilterOutLoading: boolean;
	onClickHandler: (
		operator: string,
		fieldKey: string,
		fieldValue: string,
		dataType: string | undefined,
		logType: MetricsType | undefined,
	) => () => void;
	handleChangeSelectedView?: ChangeViewFunctionType;
}

// Memoized Tree Component
const MemoizedTree = React.memo<{ treeData: DataNode[] }>(({ treeData }) => (
	<Tree
		defaultExpandAll
		showLine
		treeData={treeData}
		className="selectable-tree"
	/>
));

MemoizedTree.displayName = 'MemoizedTree';

// Body Content Component
const BodyContent: React.FC<{
	fieldData: Record<string, string>;
	record: DataType;
	bodyHtml: { __html: string };
	textToCopy: string;
	handleChangeSelectedView?: ChangeViewFunctionType;
}> = React.memo(
	({ fieldData, record, bodyHtml, textToCopy, handleChangeSelectedView }) => {
		const { isLoading, treeData, error } = useAsyncJSONProcessing(
			fieldData.value,
			record.field === 'body',
			handleChangeSelectedView,
		);

		// Show JSON tree if available, otherwise show HTML content
		if (record.field === 'body' && treeData) {
			return <MemoizedTree treeData={treeData} />;
		}

		if (record.field === 'body' && isLoading) {
			return (
				<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
					<Spin size="small" />
					<span style={{ color: Color.BG_SIENNA_400 }}>Processing JSON...</span>
				</div>
			);
		}

		if (record.field === 'body' && error) {
			return (
				<span
					style={{ color: Color.BG_SIENNA_400, whiteSpace: 'pre-wrap', tabSize: 4 }}
				>
					Error parsing Body JSON
				</span>
			);
		}

		if (record.field === 'body') {
			return (
				<CopyClipboardHOC entityKey="body" textToCopy={textToCopy}>
					<span
						style={{ color: Color.BG_SIENNA_400, whiteSpace: 'pre-wrap', tabSize: 4 }}
					>
						<span dangerouslySetInnerHTML={bodyHtml} />
					</span>
				</CopyClipboardHOC>
			);
		}

		return null;
	},
);

BodyContent.displayName = 'BodyContent';

export default function TableViewActions(
	props: ITableViewActionsProps,
): React.ReactElement {
	const {
		fieldData,
		record,
		isListViewPanel,
		isfilterInLoading,
		isfilterOutLoading,
		onClickHandler,
		handleChangeSelectedView,
	} = props;

	const { pathname } = useLocation();
	const { stagedQuery, updateQueriesData } = useQueryBuilder();
	const viewName = useGetSearchQueryParam(QueryParams.viewName) || '';
	const { dataType, logType: fieldType } = getFieldAttributes(record.field);

	// there is no option for where clause in old logs explorer and live logs page
	const isOldLogsExplorerOrLiveLogsPage = useMemo(
		() => pathname === ROUTES.OLD_LOGS_EXPLORER || pathname === ROUTES.LIVE_LOGS,
		[pathname],
	);

	const [isOpen, setIsOpen] = useState<boolean>(false);

	const { formatTimezoneAdjustedTimestamp } = useTimezone();

	// Memoize bodyHtml computation
	const bodyHtml = useMemo(() => {
		if (record.field !== 'body') {
			return { __html: '' };
		}

		return {
			__html: getSanitizedLogBody(record.value, { shouldEscapeHtml: true }),
		};
	}, [record.field, record.value]);

	const fieldFilterKey = filterKeyForField(fieldData.field);

	const handleGroupByAttribute = useCallback((): void => {
		if (!stagedQuery) {
			return;
		}
		const normalizedDataType: DataTypes | undefined =
			dataType && Object.values(DataTypes).includes(dataType as DataTypes)
				? (dataType as DataTypes)
				: undefined;

		const updatedQuery = updateQueriesData(
			stagedQuery,
			'queryData',
			(item, index) => {
				// Only add groupBy for index 0
				if (index === 0) {
					const newGroupByItem: BaseAutocompleteData = {
						key: fieldFilterKey,
						type: fieldType || '',
						dataType: normalizedDataType,
					};

					const updatedGroupBy = [...(item.groupBy || []), newGroupByItem];

					return { ...item, groupBy: updatedGroupBy };
				}

				return item;
			},
		);

		const queryData: ICurrentQueryData = {
			name: viewName,
			id: updatedQuery.id,
			query: updatedQuery,
		};

		handleChangeSelectedView?.(ExplorerViews.TIMESERIES, queryData);
	}, [
		stagedQuery,
		updateQueriesData,
		fieldFilterKey,
		fieldType,
		dataType,
		handleChangeSelectedView,
		viewName,
	]);

	const handleReplaceFilter = useCallback((): void => {
		if (!stagedQuery) {
			return;
		}
		const normalizedDataType: DataTypes | undefined =
			dataType && Object.values(DataTypes).includes(dataType as DataTypes)
				? (dataType as DataTypes)
				: undefined;

		const updatedQuery = updateQueriesData(
			stagedQuery,
			'queryData',
			(item, index) => {
				// Only replace filters for index 0
				if (index === 0) {
					const newFilterItem: BaseAutocompleteData = {
						key: fieldFilterKey,
						type: fieldType || '',
						dataType: normalizedDataType,
					};

					// Create new filter items array with single IN filter
					const newFilters = {
						items: [
							{
								id: '',
								key: newFilterItem,
								op: OPERATORS.IN,
								value: [parseFieldValue(fieldData.value)],
							},
						],
						op: 'AND',
					};

					// Clear the expression and update filters
					return {
						...item,
						filters: newFilters,
						filter: { expression: '' },
					};
				}

				return item;
			},
		);

		const queryData: ICurrentQueryData = {
			name: viewName,
			id: updatedQuery.id,
			query: updatedQuery,
		};

		handleChangeSelectedView?.(ExplorerViews.LIST, queryData);
	}, [
		stagedQuery,
		updateQueriesData,
		fieldFilterKey,
		fieldType,
		dataType,
		fieldData,
		handleChangeSelectedView,
		viewName,
	]);

	// Memoize textToCopy computation
	const textToCopy = useMemo(() => {
		let text = fieldData.value;
		try {
			text = text.replace(/^"|"$/g, '');
		} catch (error) {
			console.error(
				'Failed to remove starting and ending quotes from the value',
				error,
			);
		}
		// If the value is valid JSON (object or array), pretty-print it for copying
		try {
			const parsed = JSON.parse(text);
			if (typeof parsed === 'object' && parsed !== null) {
				return JSON.stringify(parsed, null, 2);
			}
		} catch {
			// not JSON, return as-is
		}
		return text;
	}, [fieldData.value]);

	// Memoize cleanTimestamp computation
	const cleanTimestamp = useMemo(() => {
		if (record.field !== 'timestamp') {
			return '';
		}
		return fieldData.value.replace(/^["']|["']$/g, '');
	}, [record.field, fieldData.value]);

	const renderFieldContent = useCallback((): JSX.Element => {
		const commonStyles: React.CSSProperties = {
			color: Color.BG_SIENNA_400,
			whiteSpace: 'pre-wrap',
			tabSize: 4,
		};

		switch (record.field) {
			case 'body':
				return (
					<BodyContent
						fieldData={fieldData}
						record={record}
						bodyHtml={bodyHtml}
						textToCopy={textToCopy}
						handleChangeSelectedView={handleChangeSelectedView}
					/>
				);

			case 'timestamp':
				return (
					<span style={commonStyles}>
						{formatTimezoneAdjustedTimestamp(
							cleanTimestamp,
							DATE_TIME_FORMATS.UTC_US_MS,
						)}
					</span>
				);

			default:
				return (
					<span style={commonStyles}>{removeEscapeCharacters(fieldData.value)}</span>
				);
		}
	}, [
		record,
		fieldData,
		bodyHtml,
		textToCopy,
		handleChangeSelectedView,
		formatTimezoneAdjustedTimestamp,
		cleanTimestamp,
	]);

	// Early return for body field with async processing
	if (record.field === 'body') {
		return (
			<div className={cx('value-field', isOpen ? 'open-popover' : '')}>
				<BodyContent
					fieldData={fieldData}
					record={record}
					bodyHtml={bodyHtml}
					textToCopy={textToCopy}
					handleChangeSelectedView={handleChangeSelectedView}
				/>
				{!isListViewPanel &&
					!RESTRICTED_SELECTED_FIELDS.includes(fieldFilterKey) && (
						<span className="action-btn">
							<Tooltip title="Filter for value" mouseLeaveDelay={0}>
								<Button
									className="filter-btn periscope-btn"
									icon={
										isfilterInLoading ? (
											<Spin size="small" />
										) : (
											<ArrowDownToDot size={14} style={{ transform: 'rotate(90deg)' }} />
										)
									}
									onClick={onClickHandler(
										OPERATORS['='],
										fieldFilterKey,
										parseFieldValue(fieldData.value),
										dataType,
										fieldType,
									)}
								/>
							</Tooltip>
							<Tooltip title="Filter out value" mouseLeaveDelay={0}>
								<Button
									className="filter-btn periscope-btn"
									icon={
										isfilterOutLoading ? (
											<Spin size="small" />
										) : (
											<ArrowUpFromDot size={14} style={{ transform: 'rotate(90deg)' }} />
										)
									}
									onClick={onClickHandler(
										OPERATORS['!='],
										fieldFilterKey,
										parseFieldValue(fieldData.value),
										dataType,
										fieldType,
									)}
								/>
							</Tooltip>
							{!isOldLogsExplorerOrLiveLogsPage && (
								<Popover
									open={isOpen}
									onOpenChange={setIsOpen}
									arrow={false}
									content={
										<div data-log-detail-ignore="true">
											<Button
												className="more-filter-actions"
												type="text"
												icon={<GroupByIcon />}
												onClick={handleGroupByAttribute}
											>
												Group By Attribute
											</Button>
											<Button
												className="more-filter-actions"
												type="text"
												icon={<RefreshCw size={14} />}
												onClick={handleReplaceFilter}
											>
												Replace filters with this value
											</Button>
										</div>
									}
									rootClassName="table-view-actions-content"
									trigger="hover"
									placement="bottomLeft"
								>
									<Button
										icon={<Ellipsis size={14} />}
										className="filter-btn periscope-btn"
									/>
								</Popover>
							)}
						</span>
					)}
			</div>
		);
	}

	return (
		<div className={cx('value-field', isOpen ? 'open-popover' : '')}>
			<CopyClipboardHOC entityKey={fieldFilterKey} textToCopy={textToCopy}>
				{renderFieldContent()}
			</CopyClipboardHOC>
			{!isListViewPanel &&
				!RESTRICTED_SELECTED_FIELDS.includes(fieldFilterKey) && (
					<span className="action-btn">
						<Tooltip title="Filter for value" mouseLeaveDelay={0}>
							<Button
								className="filter-btn periscope-btn"
								icon={
									isfilterInLoading ? (
										<Spin size="small" />
									) : (
										<ArrowDownToDot size={14} style={{ transform: 'rotate(90deg)' }} />
									)
								}
								onClick={onClickHandler(
									OPERATORS['='],
									fieldFilterKey,
									parseFieldValue(fieldData.value),
									dataType,
									fieldType,
								)}
							/>
						</Tooltip>
						<Tooltip title="Filter out value" mouseLeaveDelay={0}>
							<Button
								className="filter-btn periscope-btn"
								icon={
									isfilterOutLoading ? (
										<Spin size="small" />
									) : (
										<ArrowUpFromDot size={14} style={{ transform: 'rotate(90deg)' }} />
									)
								}
								onClick={onClickHandler(
									OPERATORS['!='],
									fieldFilterKey,
									parseFieldValue(fieldData.value),
									dataType,
									fieldType,
								)}
							/>
						</Tooltip>
						{!isOldLogsExplorerOrLiveLogsPage && (
							<Popover
								open={isOpen}
								onOpenChange={setIsOpen}
								arrow={false}
								content={
									<div data-log-detail-ignore="true">
										<Button
											className="more-filter-actions"
											type="text"
											icon={<GroupByIcon />}
											onClick={handleGroupByAttribute}
										>
											Group By Attribute
										</Button>
										<Button
											className="more-filter-actions"
											type="text"
											icon={<RefreshCw size={14} />}
											onClick={handleReplaceFilter}
										>
											Replace filters with this value
										</Button>
									</div>
								}
								rootClassName="table-view-actions-content"
								trigger="hover"
								placement="bottomLeft"
							>
								<Button
									icon={<Ellipsis size={14} />}
									className="filter-btn periscope-btn"
								/>
							</Popover>
						)}
					</span>
				)}
		</div>
	);
}

TableViewActions.defaultProps = {
	handleChangeSelectedView: undefined,
};
