import { Fragment, useRef } from 'react';

import type { AppBskyFeedDefs, AppBskyGraphDefs } from '@atcute/bluesky';
import { parseCanonicalResourceUri } from '@atcute/lexicons/syntax';

import { clsx } from 'clsx';

import { getPostRecord } from '#/lib/api/record-casts';
import { listTarget, profileTarget } from '#/lib/routes/targets';

import { type ThreadgateAllowUISetting, threadgateViewToAllowUISetting } from '#/state/queries/threadgate';

import { Trans } from '#/locale/Trans';

import * as Dialog from '#/components/Dialog';
import {
	PostInteractionSettingsDialog,
	usePrefetchPostInteractionSettings,
} from '#/components/dialogs/PostInteractionSettingsDialog';
import { Stack } from '#/components/Stack';
import { Text } from '#/components/Text';
import { InlineLinkText } from '#/components/web/Link';

import TinyChevronDownIcon from '#/icons/central/ChevronBottom_round_outlined_radius1_stroke2.svg';
import CircleBanSignIcon from '#/icons/central/CircleBanSign_round_outlined_radius1_stroke2.svg';
import EarthIcon from '#/icons/central/Earth_round_outlined_radius1_stroke2.svg';
import GroupIcon from '#/icons/central/Group3_round_outlined_radius1_stroke2.svg';
import { m } from '#/paraglide/messages';

import * as css from './WhoCanReply.css';

interface WhoCanReplyProps {
	post: AppBskyFeedDefs.PostView;
	isThreadAuthor: boolean;
}

export function WhoCanReply({ post, isThreadAuthor }: WhoCanReplyProps) {
	const infoDialogHandle = Dialog.useDialogHandle();
	const editDialogHandle = Dialog.useDialogHandle();

	/*
	 * `WhoCanReply` is only used for root posts atm, in case this changes
	 * unexpectedly, we should check to make sure it's for sure the root URI.
	 */
	const record = getPostRecord(post);
	const rootUri = record.reply?.root?.uri ?? post.uri;
	const settings = threadgateViewToAllowUISetting(post.threadgate);

	const prefetchPostInteractionSettings = usePrefetchPostInteractionSettings({
		postUri: post.uri,
		rootPostUri: rootUri,
	});
	const prefetchPromise = useRef<Promise<void>>(Promise.resolve());

	const prefetch = () => {
		prefetchPromise.current = prefetchPostInteractionSettings();
	};

	const anyoneCanReply = settings.length === 1 && settings[0]!.type === 'everybody';
	const noOneCanReply = settings.length === 1 && settings[0]!.type === 'nobody';
	const description = anyoneCanReply
		? m['components.whoCanReply.summary.everybody.label']()
		: noOneCanReply
			? m['components.whoCanReply.summary.disabled.label']()
			: m['components.whoCanReply.summary.some']();

	const onPressOpen = () => {
		if (isThreadAuthor) {
			// wait briefly for prefetch, then open while showing the spinner.
			void Promise.race([prefetchPromise.current, new Promise((res) => setTimeout(res, 200))]).finally(() => {
				editDialogHandle.open(null);
			});
		} else {
			infoDialogHandle.open(null);
		}
	};

	return (
		<>
			<button
				type="button"
				aria-label={
					isThreadAuthor ? m['components.whoCanReply.edit']() : m['common.interaction.whoCanReply']()
				}
				className={clsx(css.trigger, isThreadAuthor && css.triggerAuthor)}
				onClick={onPressOpen}
				// prefetch the interaction settings so the edit dialog opens without a spinner
				onMouseEnter={isThreadAuthor ? prefetch : undefined}
			>
				<Icon settings={settings} />
				<Text className={css.label} size="md_sub" color={isThreadAuthor ? 'textLink' : 'textContrastMedium'}>
					{description}
				</Text>
				{isThreadAuthor && <TinyChevronDownIcon className={css.tinyChevronDownIcon} />}
			</button>
			{isThreadAuthor ? (
				<PostInteractionSettingsDialog
					postUri={post.uri}
					rootPostUri={rootUri}
					handle={editDialogHandle}
					initialThreadgateView={post.threadgate}
				/>
			) : (
				<WhoCanReplyDialog
					handle={infoDialogHandle}
					post={post}
					settings={settings}
					embeddingDisabled={!!post.viewer?.embeddingDisabled}
				/>
			)}
		</>
	);
}

function Icon({ settings }: { settings: ThreadgateAllowUISetting[] }) {
	const isEverybody = settings.every((setting) => setting.type === 'everybody');
	const isNobody = !!settings.find((gate) => gate.type === 'nobody');
	const IconComponent = isEverybody ? EarthIcon : isNobody ? CircleBanSignIcon : GroupIcon;
	return <IconComponent className={css.gateIcon} />;
}

function WhoCanReplyDialog({
	handle,
	post,
	settings,
	embeddingDisabled,
}: {
	handle: Dialog.DialogHandle;
	post: AppBskyFeedDefs.PostView;
	settings: ThreadgateAllowUISetting[];
	embeddingDisabled: boolean;
}) {
	return (
		<Dialog.Root handle={handle}>
			<Dialog.Popup size="narrow">
				<Stack gap="sm">
					<Dialog.TitleRow>
						<Dialog.Title>{m['components.whoCanReply.title']()}</Dialog.Title>
						<Dialog.Close />
					</Dialog.TitleRow>
					<Rules post={post} settings={settings} embeddingDisabled={embeddingDisabled} />
				</Stack>
			</Dialog.Popup>
		</Dialog.Root>
	);
}

function Rules({
	post,
	settings,
	embeddingDisabled,
}: {
	post: AppBskyFeedDefs.PostView;
	settings: ThreadgateAllowUISetting[];
	embeddingDisabled: boolean;
}) {
	return (
		<>
			<Text size="md" color="textContrastMedium">
				{settings.length === 0 ? (
					m['components.whoCanReply.summary.unknown']()
				) : settings[0]!.type === 'everybody' ? (
					m['components.whoCanReply.summary.everybody.description']()
				) : settings[0]!.type === 'nobody' ? (
					m['components.whoCanReply.summary.disabled.description']()
				) : (
					<Trans
						message={m['components.whoCanReply.rules.template']}
						markup={{
							t0: () => (
								<>
									{settings.map((rule, i) => (
										// oxlint-disable-next-line react/no-array-index-key -- fixed order
										<Fragment key={`rule-${i}`}>
											<Rule rule={rule} post={post} lists={post.threadgate!.lists} />
											<Separator i={i} length={settings.length} />
										</Fragment>
									))}
								</>
							),
						}}
					/>
				)}{' '}
			</Text>
			{embeddingDisabled && (
				<Text size="md" color="textContrastMedium">
					{m['components.whoCanReply.quote.noOne']()}
				</Text>
			)}
		</>
	);
}

function Rule({
	rule,
	post,
	lists,
}: {
	rule: ThreadgateAllowUISetting;
	post: AppBskyFeedDefs.PostView;
	lists: AppBskyGraphDefs.ListViewBasic[] | undefined;
}) {
	if (rule.type === 'mention') {
		return m['components.whoCanReply.rules.mentioned']();
	}
	if (rule.type === 'followers') {
		return (
			<Trans
				message={m['components.whoCanReply.rules.following']}
				inputs={{ handle: post.author.handle }}
				markup={{
					t0: ({ children }) => (
						<InlineLinkText
							label={`@${post.author.handle}`}
							size="md_sub"
							to={profileTarget(post.author.did)}
						>
							{children}
						</InlineLinkText>
					),
				}}
			/>
		);
	}
	if (rule.type === 'following') {
		return (
			<Trans
				message={m['components.whoCanReply.rules.followedBy']}
				inputs={{ handle: post.author.handle }}
				markup={{
					t0: ({ children }) => (
						<InlineLinkText
							label={`@${post.author.handle}`}
							size="md_sub"
							to={profileTarget(post.author.did)}
						>
							{children}
						</InlineLinkText>
					),
				}}
			/>
		);
	}
	if (rule.type === 'list') {
		const list = lists?.find((l) => l.uri === rule.list);
		if (list) {
			const listUrip = parseCanonicalResourceUri(list.uri);
			return (
				<Trans
					message={m['components.whoCanReply.rules.listMembers']}
					inputs={{ name: list.name }}
					markup={{
						t0: ({ children }) => (
							<InlineLinkText label={list.name} size="md_sub" to={listTarget(listUrip.repo, listUrip.rkey)}>
								{children}
							</InlineLinkText>
						),
					}}
				/>
			);
		}
	}
}

function Separator({ i, length }: { i: number; length: number }) {
	if (length < 2 || i === length - 1) {
		return null;
	}
	if (i === length - 2) {
		return (
			<>
				{length > 2 ? ',' : ''} {m['components.whoCanReply.rules.and']()}{' '}
			</>
		);
	}
	return <>, </>;
}
