import { type KeyboardEvent, useState } from 'react';

import type { AppBskyActorDefs } from '@atcute/bluesky';

import { errorMessage } from '#/lib/errors';

import { useUpsertMutedWordsMutation } from '#/state/queries/preferences';
import { sanitizeMutedWordValue } from '#/state/queries/preferences/agent';

import * as Dialog from '#/components/Dialog';
import * as styles from '#/components/dialogs/MutedWords.css';
import type * as Select from '#/components/Select';
import * as Settings from '#/components/SettingsCards';
import { Spinner } from '#/components/Spinner';
import { Stack } from '#/components/Stack';
import { Text } from '#/components/Text';
import * as TextField from '#/components/TextField';
import * as Toast from '#/components/Toast';
import { Button, ButtonIcon, ButtonText } from '#/components/web/Button';

import Plus from '#/icons/central/PlusLarge_round_outlined_radius1_stroke2.svg';
import { m } from '#/paraglide/messages';

const ONE_DAY = 24 * 60 * 60 * 1000;

export function MutedWordsDialog({ handle }: { handle: Dialog.DialogHandle }) {
	return (
		<Dialog.Root handle={handle}>
			<Dialog.Popup size="narrow">
				<DialogInner handle={handle} />
			</Dialog.Popup>
		</Dialog.Root>
	);
}

function DialogInner({ handle }: { handle: Dialog.DialogHandle }) {
	const { isPending, mutateAsync: addMutedWord } = useUpsertMutedWordsMutation();
	const [field, setField] = useState('');
	const [target, setTarget] = useState<'content' | 'tag'>('content');
	const [duration, setDuration] = useState<'24_hours' | '30_days' | '7_days' | 'forever'>('forever');
	const [excludeFollowing, setExcludeFollowing] = useState(false);
	const [error, setError] = useState('');

	const durationItems: Select.SelectItem<typeof duration>[] = [
		{ label: m['common.time.forever'](), value: 'forever' },
		{ label: m['common.time.hours24'](), value: '24_hours' },
		{ label: m['common.time.days7'](), value: '7_days' },
		{ label: m['common.time.days30'](), value: '30_days' },
	];
	const targetItems: Select.SelectItem<typeof target>[] = [
		{ label: m['components.dialogs.mutedWord.textAndTags'](), value: 'content' },
		{ label: m['components.dialogs.mutedWord.tagsOnly'](), value: 'tag' },
	];

	const submit = async () => {
		const sanitizedValue = sanitizeMutedWordValue(field);
		const surfaces: AppBskyActorDefs.MutedWord['targets'] =
			target === 'content' ? ['tag', 'content'] : ['tag'];
		const actorTarget = excludeFollowing ? 'exclude-following' : 'all';

		const now = Date.now();
		// undefined evaluates to 'forever'
		let expiresAt: string | undefined;
		if (duration === '24_hours') {
			expiresAt = new Date(now + ONE_DAY).toISOString();
		} else if (duration === '7_days') {
			expiresAt = new Date(now + 7 * ONE_DAY).toISOString();
		} else if (duration === '30_days') {
			expiresAt = new Date(now + 30 * ONE_DAY).toISOString();
		}

		if (!sanitizedValue || !surfaces.length) {
			setField('');
			setError(m['components.dialogs.mutedWord.invalidError']());
			return;
		}

		try {
			// send raw value and rely on SDK as sanitization source of truth
			await addMutedWord([{ value: field, targets: surfaces, actorTarget, expiresAt }]);
			Toast.show(m['common.mute.status'](), { type: 'success' });
			handle.close();
		} catch (e) {
			const message = errorMessage(e);
			setError(message);
		}
	};

	const onKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
		if (e.key === 'Enter') {
			e.preventDefault();
			void submit();
		}
	};

	return (
		<Stack gap="lg">
			<Stack gap="xs">
				<Dialog.TitleRow>
					<Dialog.Title>{m['common.mutedWord.action.add']()}</Dialog.Title>
					<Dialog.Close />
				</Dialog.TitleRow>

				<Text size="md_sub" color="textContrastMedium">
					{m['components.dialogs.mutedWord.description']()}
				</Text>
			</Stack>

			<TextField.Root>
				<TextField.Input
					autoCapitalize="none"
					autoComplete="off"
					autoFocus
					label={m['components.dialogs.mutedWord.inputPlaceholder']()}
					placeholder={m['components.dialogs.mutedWord.inputPlaceholder']()}
					value={field}
					onChangeText={(value) => {
						if (error) {
							setError('');
						}
						setField(value);
					}}
					onKeyDown={onKeyDown}
				/>
			</TextField.Root>

			<Settings.Section>
				<Settings.SelectRow
					items={durationItems}
					label={m['components.dialogs.mutedWord.selectDuration']()}
					onValueChange={setDuration}
					value={duration}
				>
					<Settings.Label titleText={m['components.dialogs.mutedWord.duration']()} />
				</Settings.SelectRow>
				<Settings.SelectRow
					items={targetItems}
					label={m['components.dialogs.mutedWord.selectTargets']()}
					onValueChange={setTarget}
					value={target}
				>
					<Settings.Label titleText={m['components.dialogs.mutedWord.muteIn']()} />
				</Settings.SelectRow>
				<Settings.SwitchRow
					label={m['components.dialogs.mutedWord.excludeFollows']()}
					onChange={setExcludeFollowing}
					value={excludeFollowing}
				>
					<Settings.Label titleText={m['components.dialogs.mutedWord.excludeFollowsLabel']()} />
				</Settings.SwitchRow>
			</Settings.Section>

			<Button
				className={styles.addButton}
				color="primary"
				disabled={isPending || !field}
				label={m['components.dialogs.mutedWord.addSubmit']()}
				size="large"
				onClick={() => void submit()}
			>
				<ButtonText>{m['common.action.add']()}</ButtonText>
				{isPending ? (
					<Spinner color="white" label={m['components.dialogs.list.adding']()} size="sm" />
				) : (
					<ButtonIcon icon={Plus} />
				)}
			</Button>

			{error && (
				<div className={styles.error}>
					<Text className={styles.errorText} color="white">
						{error}
					</Text>
				</div>
			)}
		</Stack>
	);
}
