import { LoadingOutlined } from '@ant-design/icons';
import {
	Button,
	Col,
	Divider,
	Modal,
	notification,
	Row,
	Spin,
	Typography,
} from 'antd';
import setRetentionApi from 'api/settings/setRetention';
import TextToolTip from 'components/TextToolTip';
import useComponentPermission from 'hooks/useComponentPermission';
import find from 'lodash-es/find';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { UseQueryResult } from 'react-query';
import { useSelector } from 'react-redux';
import { useInterval } from 'react-use';
import { AppState } from 'store/reducers';
import { ErrorResponse, SuccessResponse } from 'types/api';
import {
	IDiskType,
	PayloadProps as GetDisksPayload,
} from 'types/api/disks/getDisks';
import { TTTLType } from 'types/api/settings/common';
import {
	PayloadPropsMetrics as GetRetentionPeriodMetricsPayload,
	PayloadPropsTraces as GetRetentionPeriodTracesPayload,
} from 'types/api/settings/getRetention';
import AppReducer from 'types/reducer/app';

import Retention from './Retention';
import StatusMessage from './StatusMessage';
import { ActionItemsContainer, ErrorText, ErrorTextContainer } from './styles';

type NumberOrNull = number | null;

function GeneralSettings({
	metricsTtlValuesPayload,
	tracesTtlValuesPayload,
	getAvailableDiskPayload,
	metricsTtlValuesRefetch,
	tracesTtlValuesRefetch,
}: GeneralSettingsProps): JSX.Element {
	const { t } = useTranslation(['generalSettings']);
	const [modalMetrics, setModalMetrics] = useState<boolean>(false);
	const [postApiLoadingMetrics, setPostApiLoadingMetrics] = useState<boolean>(
		false,
	);
	const [postApiLoadingTraces, setPostApiLoadingTraces] = useState<boolean>(
		false,
	);
	const [modalTraces, setModalTraces] = useState<boolean>(false);
	const [availableDisks] = useState<IDiskType[]>(getAvailableDiskPayload);

	const [metricsCurrentTTLValues, setMetricsCurrentTTLValues] = useState(
		metricsTtlValuesPayload,
	);
	const [tracesCurrentTTLValues, setTracesCurrentTTLValues] = useState(
		tracesTtlValuesPayload,
	);

	const { role } = useSelector<AppState, AppReducer>((state) => state.app);

	const [setRetentionPermission] = useComponentPermission(
		['set_retention_period'],
		role,
	);

	const [
		metricsTotalRetentionPeriod,
		setMetricsTotalRetentionPeriod,
	] = useState<NumberOrNull>(null);
	const [
		metricsS3RetentionPeriod,
		setMetricsS3RetentionPeriod,
	] = useState<NumberOrNull>(null);
	const [
		tracesTotalRetentionPeriod,
		setTracesTotalRetentionPeriod,
	] = useState<NumberOrNull>(null);
	const [
		tracesS3RetentionPeriod,
		setTracesS3RetentionPeriod,
	] = useState<NumberOrNull>(null);

	useEffect(() => {
		if (metricsCurrentTTLValues) {
			setMetricsTotalRetentionPeriod(
				metricsCurrentTTLValues.metrics_ttl_duration_hrs,
			);
			setMetricsS3RetentionPeriod(
				metricsCurrentTTLValues.metrics_move_ttl_duration_hrs
					? metricsCurrentTTLValues.metrics_move_ttl_duration_hrs
					: null,
			);
		}
	}, [metricsCurrentTTLValues]);

	useEffect(() => {
		if (tracesCurrentTTLValues) {
			setTracesTotalRetentionPeriod(
				tracesCurrentTTLValues.traces_ttl_duration_hrs,
			);
			setTracesS3RetentionPeriod(
				tracesCurrentTTLValues.traces_move_ttl_duration_hrs
					? tracesCurrentTTLValues.traces_move_ttl_duration_hrs
					: null,
			);
		}
	}, [tracesCurrentTTLValues]);

	useInterval(
		async (): Promise<void> => {
			if (metricsTtlValuesPayload.status === 'pending') {
				metricsTtlValuesRefetch();
			}
		},
		metricsTtlValuesPayload.status === 'pending' ? 1000 : null,
	);

	useInterval(
		async (): Promise<void> => {
			if (tracesTtlValuesPayload.status === 'pending') {
				tracesTtlValuesRefetch();
			}
		},
		tracesTtlValuesPayload.status === 'pending' ? 1000 : null,
	);

	const onModalToggleHandler = (type: TTTLType): void => {
		if (type === 'metrics') setModalMetrics((modal) => !modal);
		if (type === 'traces') setModalTraces((modal) => !modal);
	};
	const onPostApiLoadingHandler = (type: TTTLType): void => {
		if (type === 'metrics') setPostApiLoadingMetrics((modal) => !modal);
		if (type === 'traces') setPostApiLoadingTraces((modal) => !modal);
	};

	const onClickSaveHandler = useCallback(
		(type: TTTLType) => {
			if (!setRetentionPermission) {
				notification.error({
					message: `Sorry you don't have permission to make these changes`,
				});
				return;
			}
			onModalToggleHandler(type);
		},
		[setRetentionPermission],
	);

	const s3Enabled = useMemo(
		() => !!find(availableDisks, (disks: IDiskType) => disks?.type === 's3'),
		[availableDisks],
	);

	const [isMetricsSaveDisabled, isTracesSaveDisabled, errorText] = useMemo((): [
		boolean,
		boolean,
		string,
		// eslint-disable-next-line sonarjs/cognitive-complexity
	] => {
		// Various methods to return dynamic error message text.
		const messages = {
			compareError: (name: string | number): string =>
				t('retention_comparison_error', { name }),
			nullValueError: (name: string | number): string =>
				t('retention_null_value_error', { name }),
		};

		// Defaults to button not disabled and empty error message text.
		let isMetricsSaveDisabled = false;
		let isTracesSaveDisabled = false;
		let errorText = '';

		if (s3Enabled) {
			if (
				(metricsTotalRetentionPeriod || metricsS3RetentionPeriod) &&
				Number(metricsTotalRetentionPeriod) <= Number(metricsS3RetentionPeriod)
			) {
				isMetricsSaveDisabled = true;
				errorText = messages.compareError('metrics');
			} else if (
				(tracesTotalRetentionPeriod || tracesS3RetentionPeriod) &&
				Number(tracesTotalRetentionPeriod) <= Number(tracesS3RetentionPeriod)
			) {
				isTracesSaveDisabled = true;
				errorText = messages.compareError('traces');
			}
		}

		if (!metricsTotalRetentionPeriod || !tracesTotalRetentionPeriod) {
			isMetricsSaveDisabled = true;
			isTracesSaveDisabled = true;
			if (!metricsTotalRetentionPeriod && !tracesTotalRetentionPeriod) {
				errorText = messages.nullValueError('metrics and traces');
			} else if (!metricsTotalRetentionPeriod) {
				errorText = messages.nullValueError('metrics');
			} else if (!tracesTotalRetentionPeriod) {
				errorText = messages.nullValueError('traces');
			}
		}
		if (
			metricsCurrentTTLValues?.metrics_ttl_duration_hrs ===
				metricsTotalRetentionPeriod &&
			metricsCurrentTTLValues.metrics_move_ttl_duration_hrs ===
				metricsS3RetentionPeriod
		)
			isMetricsSaveDisabled = true;

		if (
			tracesCurrentTTLValues.traces_ttl_duration_hrs ===
				tracesTotalRetentionPeriod &&
			tracesCurrentTTLValues.traces_move_ttl_duration_hrs ===
				tracesS3RetentionPeriod
		)
			isTracesSaveDisabled = true;

		return [isMetricsSaveDisabled, isTracesSaveDisabled, errorText];
	}, [
		metricsCurrentTTLValues.metrics_move_ttl_duration_hrs,
		metricsCurrentTTLValues?.metrics_ttl_duration_hrs,
		metricsS3RetentionPeriod,
		metricsTotalRetentionPeriod,
		s3Enabled,
		t,
		tracesCurrentTTLValues.traces_move_ttl_duration_hrs,
		tracesCurrentTTLValues.traces_ttl_duration_hrs,
		tracesS3RetentionPeriod,
		tracesTotalRetentionPeriod,
	]);

	// eslint-disable-next-line sonarjs/cognitive-complexity
	const onOkHandler = async (type: TTTLType): Promise<void> => {
		try {
			onPostApiLoadingHandler(type);
			const setTTLResponse = await setRetentionApi({
				type,
				totalDuration: `${
					(type === 'metrics'
						? metricsTotalRetentionPeriod
						: tracesTotalRetentionPeriod) || -1
				}h`,
				coldStorage: s3Enabled ? 's3' : null,
				toColdDuration: `${
					(type === 'metrics'
						? metricsS3RetentionPeriod
						: tracesS3RetentionPeriod) || -1
				}h`,
			});
			let hasSetTTLFailed = false;
			if (setTTLResponse.statusCode === 409) {
				hasSetTTLFailed = true;
				notification.error({
					message: 'Error',
					description: t('retention_request_race_condition'),
					placement: 'topRight',
				});
			}

			if (type === 'metrics') {
				metricsTtlValuesRefetch();

				if (!hasSetTTLFailed)
					// Updates the currentTTL Values in order to avoid pushing the same values.
					setMetricsCurrentTTLValues({
						metrics_ttl_duration_hrs: metricsTotalRetentionPeriod || -1,
						metrics_move_ttl_duration_hrs: metricsS3RetentionPeriod || -1,
						status: '',
					});
			} else if (type === 'traces') {
				tracesTtlValuesRefetch();

				if (!hasSetTTLFailed)
					// Updates the currentTTL Values in order to avoid pushing the same values.
					setTracesCurrentTTLValues({
						traces_ttl_duration_hrs: tracesTotalRetentionPeriod || -1,
						traces_move_ttl_duration_hrs: tracesS3RetentionPeriod || -1,
						status: '',
					});
			}
		} catch (error) {
			notification.error({
				message: 'Error',
				description: t('retention_failed_message'),
				placement: 'topRight',
			});
		}

		onPostApiLoadingHandler(type);
		onModalToggleHandler(type);
	};

	const renderConfig = [
		{
			name: 'Metrics',
			retentionFields: [
				{
					name: t('total_retention_period'),
					value: metricsTotalRetentionPeriod,
					setValue: setMetricsTotalRetentionPeriod,
				},
				{
					name: t('move_to_s3'),
					value: metricsS3RetentionPeriod,
					setValue: setMetricsS3RetentionPeriod,
					hide: !s3Enabled,
				},
			],
			save: {
				modal: modalMetrics,
				modalOpen: (): void => onClickSaveHandler('metrics'),
				apiLoading: postApiLoadingMetrics,
				saveButtonText:
					metricsTtlValuesPayload.status === 'pending' ? (
						<span>
							<Spin spinning size="small" indicator={<LoadingOutlined spin />} />{' '}
							{t('retention_save_button.pending', { name: 'metrics' })}
						</span>
					) : (
						<span>{t('retention_save_button.success')}</span>
					),
				isDisabled:
					metricsTtlValuesPayload.status === 'pending' || isMetricsSaveDisabled,
			},
			statusComponent: (
				<StatusMessage
					total_retention={metricsTtlValuesPayload.expected_metrics_ttl_duration_hrs}
					status={metricsTtlValuesPayload.status}
					s3_retention={
						metricsTtlValuesPayload.expected_metrics_move_ttl_duration_hrs
					}
				/>
			),
		},
		{
			name: 'Traces',
			retentionFields: [
				{
					name: t('total_retention_period'),
					value: tracesTotalRetentionPeriod,
					setValue: setTracesTotalRetentionPeriod,
				},
				{
					name: t('move_to_s3'),
					value: tracesS3RetentionPeriod,
					setValue: setTracesS3RetentionPeriod,
					hide: !s3Enabled,
				},
			],
			save: {
				modal: modalTraces,
				modalOpen: (): void => onClickSaveHandler('traces'),
				apiLoading: postApiLoadingTraces,
				saveButtonText:
					tracesTtlValuesPayload.status === 'pending' ? (
						<span>
							<Spin spinning size="small" indicator={<LoadingOutlined spin />} />{' '}
							{t('retention_save_button.pending', { name: 'traces' })}
						</span>
					) : (
						<span>{t('retention_save_button.success')}</span>
					),
				isDisabled:
					tracesTtlValuesPayload.status === 'pending' || isTracesSaveDisabled,
			},
			statusComponent: (
				<StatusMessage
					total_retention={tracesTtlValuesPayload.expected_traces_ttl_duration_hrs}
					status={tracesTtlValuesPayload.status}
					s3_retention={tracesTtlValuesPayload.expected_traces_move_ttl_duration_hrs}
				/>
			),
		},
	].map((category, idx, renderArr): JSX.Element | null => {
		if (
			Array.isArray(category.retentionFields) &&
			category.retentionFields.length > 0
		) {
			return (
				<React.Fragment key={category.name}>
					<Col xs={22} xl={11} key={category.name}>
						<Typography.Title level={3}>{category.name}</Typography.Title>

						{category.retentionFields.map((retentionField) => (
							<Retention
								key={retentionField.name}
								text={retentionField.name}
								retentionValue={retentionField.value}
								setRetentionValue={retentionField.setValue}
								hide={!!retentionField.hide}
							/>
						))}
						<ActionItemsContainer>
							<Button
								type="primary"
								onClick={category.save.modalOpen}
								disabled={category.save.isDisabled}
							>
								{category.save.saveButtonText}
							</Button>
							{category.statusComponent}
						</ActionItemsContainer>
						<Modal
							title={t('retention_confirmation')}
							focusTriggerAfterClose
							forceRender
							destroyOnClose
							closable
							onCancel={(): void =>
								onModalToggleHandler(category.name.toLowerCase() as TTTLType)
							}
							onOk={(): Promise<void> =>
								onOkHandler(category.name.toLowerCase() as TTTLType)
							}
							centered
							visible={category.save.modal}
							confirmLoading={category.save.apiLoading}
						>
							<Typography>
								{t('retention_confirmation_description', {
									name: category.name.toLowerCase(),
								})}
							</Typography>
						</Modal>
					</Col>
					{idx < renderArr.length && (
						<Col xs={0} xl={1} style={{ textAlign: 'center' }}>
							<Divider type="vertical" dashed style={{ height: '100%' }} />
						</Col>
					)}
				</React.Fragment>
			);
		}
		return null;
	});

	return (
		<Col xs={24} md={22} xl={20} xxl={18} style={{ margin: 'auto' }}>
			{Element}
			<ErrorTextContainer>
				<TextToolTip
					{...{
						text: `More details on how to set retention period`,
						url: 'https://signoz.io/docs/userguide/retention-period/',
					}}
				/>
				{errorText && <ErrorText>{errorText}</ErrorText>}
			</ErrorTextContainer>

			<Row justify="space-around">{renderConfig}</Row>
		</Col>
	);
}

interface GeneralSettingsProps {
	getAvailableDiskPayload: GetDisksPayload;
	metricsTtlValuesPayload: GetRetentionPeriodMetricsPayload;
	tracesTtlValuesPayload: GetRetentionPeriodTracesPayload;
	metricsTtlValuesRefetch: UseQueryResult<
		ErrorResponse | SuccessResponse<GetRetentionPeriodMetricsPayload>
	>['refetch'];
	tracesTtlValuesRefetch: UseQueryResult<
		ErrorResponse | SuccessResponse<GetRetentionPeriodTracesPayload>
	>['refetch'];
}

export default GeneralSettings;
