import { ReactNode, useCallback, useMemo, useState } from 'react';
import { UseQueryResult } from 'react-query';
import {
	AlertOutlined,
	CloudDownloadOutlined,
	CopyOutlined,
	DeleteOutlined,
	EditFilled,
	FullscreenOutlined,
	InfoCircleOutlined,
	MoreOutlined,
	SearchOutlined,
} from '@ant-design/icons';
import { Color } from '@signozhq/design-tokens';
import { Dropdown, Input, MenuProps, Tooltip, Typography } from 'antd';
import ErrorContent from 'components/ErrorModal/components/ErrorContent';
import ErrorPopover from 'components/ErrorPopover/ErrorPopover';
import Spinner from 'components/Spinner';
import WarningPopover from 'components/WarningPopover/WarningPopover';
import { QueryParams } from 'constants/query';
import { PANEL_TYPES } from 'constants/queryBuilder';
import useGetResolvedText from 'hooks/dashboard/useGetResolvedText';
import useCreateAlerts from 'hooks/queryBuilder/useCreateAlerts';
import useComponentPermission from 'hooks/useComponentPermission';
import { useSafeNavigate } from 'hooks/useSafeNavigate';
import useUrlQuery from 'hooks/useUrlQuery';
import { RowData } from 'lib/query/createTableColumnsFromQuery';
import { isEmpty } from 'lodash-es';
import { CircleX, SquareArrowOutUpRight, X } from 'lucide-react';
import { unparse } from 'papaparse';
import { useAppContext } from 'providers/App/App';
import { SuccessResponse, Warning } from 'types/api';
import { Widgets } from 'types/api/dashboard/getAll';
import APIError from 'types/api/error';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { buildAbsolutePath } from 'utils/app';

import { errorTooltipPosition } from './config';
import { MENUITEM_KEYS_VS_LABELS, MenuItemKeys } from './contants';
import { MenuItem } from './types';
import { generateMenuList, isTWidgetOptions } from './utils';

import './WidgetHeader.styles.scss';

interface IWidgetHeaderProps {
	title: ReactNode;
	widget: Widgets;
	onView: VoidFunction;
	onDelete?: VoidFunction;
	onClone?: VoidFunction;
	queryResponse: UseQueryResult<
		SuccessResponse<MetricRangePayloadProps, unknown> & {
			warning?: Warning;
		},
		Error
	>;
	threshold?: ReactNode;
	headerMenuList?: MenuItemKeys[];
	isWarning: boolean;
	isFetchingResponse: boolean;
	tableProcessedDataRef: React.MutableRefObject<RowData[]>;
	setSearchTerm: React.Dispatch<React.SetStateAction<string>>;
}

function WidgetHeader({
	title,
	widget,
	onView,
	onDelete,
	onClone,
	queryResponse,
	threshold,
	headerMenuList,
	isWarning,
	isFetchingResponse,
	tableProcessedDataRef,
	setSearchTerm,
}: IWidgetHeaderProps): JSX.Element | null {
	const urlQuery = useUrlQuery();
	const { safeNavigate } = useSafeNavigate();
	const onEditHandler = useCallback((): void => {
		const widgetId = widget.id;
		urlQuery.set(QueryParams.widgetId, widgetId);
		urlQuery.set(QueryParams.graphType, widget.panelTypes);
		urlQuery.set(
			QueryParams.compositeQuery,
			encodeURIComponent(JSON.stringify(widget.query)),
		);
		const generatedUrl = buildAbsolutePath({
			relativePath: 'new',
			urlQueryString: urlQuery.toString(),
		});
		safeNavigate(generatedUrl);
	}, [safeNavigate, urlQuery, widget.id, widget.panelTypes, widget.query]);

	const onCreateAlertsHandler = useCreateAlerts(widget, 'dashboardView');

	const onDownloadHandler = useCallback((): void => {
		const csv = unparse(tableProcessedDataRef.current);
		const csvBlob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
		const csvUrl = URL.createObjectURL(csvBlob);
		const downloadLink = document.createElement('a');
		downloadLink.href = csvUrl;
		downloadLink.download = `${!isEmpty(title) ? title : 'table-panel'}.csv`;
		downloadLink.click();
		downloadLink.remove();
	}, [tableProcessedDataRef, title]);

	const keyMethodMapping = useMemo(
		() => ({
			[MenuItemKeys.View]: onView,
			[MenuItemKeys.Edit]: onEditHandler,
			[MenuItemKeys.Delete]: onDelete,
			[MenuItemKeys.Clone]: onClone,
			[MenuItemKeys.CreateAlerts]: onCreateAlertsHandler,
			[MenuItemKeys.Download]: onDownloadHandler,
		}),
		[
			onView,
			onEditHandler,
			onDelete,
			onClone,
			onCreateAlertsHandler,
			onDownloadHandler,
		],
	);

	const onMenuItemSelectHandler: MenuProps['onClick'] = useCallback(
		({ key }: { key: string }): void => {
			if (isTWidgetOptions(key)) {
				const functionToCall = keyMethodMapping[key];

				if (functionToCall) {
					functionToCall();
				}
			}
		},
		[keyMethodMapping],
	);
	const { user } = useAppContext();

	const [deleteWidget, editWidget] = useComponentPermission(
		['delete_widget', 'edit_widget'],
		user.role,
	);

	const actions = useMemo(
		(): MenuItem[] => [
			{
				key: MenuItemKeys.View,
				icon: <FullscreenOutlined />,
				label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.View],
				isVisible: headerMenuList?.includes(MenuItemKeys.View) || false,
				disabled: queryResponse.isFetching,
			},
			{
				key: MenuItemKeys.Edit,
				icon: <EditFilled />,
				label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Edit],
				isVisible: headerMenuList?.includes(MenuItemKeys.Edit) || false,
				disabled: !editWidget,
			},
			{
				key: MenuItemKeys.Clone,
				icon: <CopyOutlined />,
				label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Clone],
				isVisible: headerMenuList?.includes(MenuItemKeys.Clone) || false,
				disabled: !editWidget,
			},
			{
				key: MenuItemKeys.Download,
				icon: <CloudDownloadOutlined />,
				label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Download],
				isVisible: widget.panelTypes === PANEL_TYPES.TABLE,
				disabled: false,
			},
			{
				key: MenuItemKeys.Delete,
				icon: <DeleteOutlined />,
				label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Delete],
				isVisible: headerMenuList?.includes(MenuItemKeys.Delete) || false,
				disabled: !deleteWidget,
				danger: true,
			},
			{
				key: MenuItemKeys.CreateAlerts,
				icon: <AlertOutlined />,
				label: (
					<span
						style={{
							display: 'flex',
							alignItems: 'baseline',
							justifyContent: 'space-between',
						}}
					>
						{MENUITEM_KEYS_VS_LABELS[MenuItemKeys.CreateAlerts]}
						<SquareArrowOutUpRight size={10} />
					</span>
				),
				isVisible: headerMenuList?.includes(MenuItemKeys.CreateAlerts) || false,
				disabled: false,
			},
		],
		[
			headerMenuList,
			queryResponse.isFetching,
			editWidget,
			deleteWidget,
			widget.panelTypes,
		],
	);

	const updatedMenuList = useMemo(() => generateMenuList(actions), [actions]);

	const [showGlobalSearch, setShowGlobalSearch] = useState(false);

	const globalSearchAvailable = widget.panelTypes === PANEL_TYPES.TABLE;

	const menu = useMemo(
		() => ({
			items: updatedMenuList,
			onClick: onMenuItemSelectHandler,
		}),
		[updatedMenuList, onMenuItemSelectHandler],
	);

	const { truncatedText, fullText } = useGetResolvedText({
		text: widget.title as string,
		maxLength: 100,
	});

	const renderErrorMessage = useMemo(
		() => <ErrorContent error={queryResponse.error as APIError} />,
		[queryResponse.error],
	);

	if (widget.id === PANEL_TYPES.EMPTY_WIDGET) {
		return null;
	}

	return (
		<div className="widget-header-container">
			{showGlobalSearch ? (
				<Input
					addonBefore={<SearchOutlined size={14} />}
					placeholder="Search..."
					bordered={false}
					data-testid="widget-header-search-input"
					autoFocus
					addonAfter={
						<X
							size={14}
							onClick={(e): void => {
								e.stopPropagation();
								e.preventDefault();
								setSearchTerm('');
								setShowGlobalSearch(false);
							}}
							className="search-header-icons"
						/>
					}
					key={widget.id}
					onChange={(e): void => {
						setSearchTerm(e.target.value || '');
					}}
				/>
			) : (
				<>
					<div className="widget-header-title-container">
						<Tooltip title={fullText} placement="top">
							<Typography.Text
								ellipsis
								data-testid={title}
								className="widget-header-title"
							>
								{truncatedText}
							</Typography.Text>
						</Tooltip>
						{widget.description && (
							<Tooltip
								title={widget.description}
								overlayClassName="long-tooltip"
								className="info-tooltip"
								placement="right"
							>
								<InfoCircleOutlined />
							</Tooltip>
						)}
					</div>
					<div className="widget-header-actions">
						<div className="widget-api-actions">{threshold}</div>
						{isFetchingResponse && !queryResponse.isError && (
							<Spinner style={{ paddingRight: '0.25rem' }} />
						)}
						{queryResponse.isError && (
							<ErrorPopover
								content={renderErrorMessage}
								placement={errorTooltipPosition}
								overlayStyle={{ padding: 0, maxWidth: '600px' }}
								overlayInnerStyle={{ padding: 0 }}
								autoAdjustOverflow
							>
								<CircleX
									size={16}
									style={{ cursor: 'pointer' }}
									color={Color.BG_CHERRY_500}
								/>
							</ErrorPopover>
						)}

						{isWarning && queryResponse.data?.warning && (
							<WarningPopover warningData={queryResponse.data?.warning as Warning} />
						)}
						{globalSearchAvailable && (
							<SearchOutlined
								className="search-header-icons"
								onClick={(): void => setShowGlobalSearch(true)}
								data-testid="widget-header-search"
							/>
						)}

						{menu && Array.isArray(menu.items) && menu.items.length > 0 && (
							<Dropdown menu={menu} trigger={['hover']} placement="bottomRight">
								<MoreOutlined
									data-testid="widget-header-options"
									className={`widget-header-more-options ${
										globalSearchAvailable ? 'widget-header-more-options-visible' : ''
									}`}
								/>
							</Dropdown>
						)}
					</div>
				</>
			)}
		</div>
	);
}

WidgetHeader.defaultProps = {
	onDelete: undefined,
	onClone: undefined,
	threshold: undefined,
	headerMenuList: [MenuItemKeys.View],
};

export default WidgetHeader;
