<script lang="ts">
	import dayjs from 'dayjs';
	import relativeTime from 'dayjs/plugin/relativeTime';
	import isToday from 'dayjs/plugin/isToday';
	import isYesterday from 'dayjs/plugin/isYesterday';
	import localizedFormat from 'dayjs/plugin/localizedFormat';

	dayjs.extend(relativeTime);
	dayjs.extend(isToday);
	dayjs.extend(isYesterday);
	dayjs.extend(localizedFormat);

	import { getContext, onMount } from 'svelte';
	const i18n = getContext<Writable<i18nType>>('i18n');

	import { formatDate } from '$lib/utils';

	import { settings, user, shortCodesToEmojis } from '$lib/stores';
	import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants';
	import { getMessageData } from '$lib/apis/channels';

	import Markdown from '$lib/components/chat/Messages/Markdown.svelte';
	import ProfileImage from '$lib/components/chat/Messages/ProfileImage.svelte';
	import Name from '$lib/components/chat/Messages/Name.svelte';
	import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
	import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
	import Pencil from '$lib/components/icons/Pencil.svelte';
	import Tooltip from '$lib/components/common/Tooltip.svelte';
	import Textarea from '$lib/components/common/Textarea.svelte';
	import Image from '$lib/components/common/Image.svelte';
	import FileItem from '$lib/components/common/FileItem.svelte';
	import ProfilePreview from './Message/ProfilePreview.svelte';
	import ChatBubbleOvalEllipsis from '$lib/components/icons/ChatBubble.svelte';
	import FaceSmile from '$lib/components/icons/FaceSmile.svelte';
	import EmojiPicker from '$lib/components/common/EmojiPicker.svelte';
	import ChevronRight from '$lib/components/icons/ChevronRight.svelte';
	import Emoji from '$lib/components/common/Emoji.svelte';
	import Skeleton from '$lib/components/chat/Messages/Skeleton.svelte';
	import ArrowUpLeftAlt from '$lib/components/icons/ArrowUpLeftAlt.svelte';
	import PinSlash from '$lib/components/icons/PinSlash.svelte';
	import Pin from '$lib/components/icons/Pin.svelte';

	export let className = '';

	export let message;
	export let channel;

	export let showUserProfile = true;
	export let thread = false;

	export let replyToMessage = false;
	export let disabled = false;
	export let pending = false;

	export let onDelete: Function = () => {};
	export let onEdit: Function = () => {};
	export let onReply: Function = () => {};
	export let onPin: Function = () => {};
	export let onThread: Function = () => {};
	export let onReaction: Function = () => {};

	let showButtons = false;

	let edit = false;
	let editedContent = null;
	let showDeleteConfirmDialog = false;

	const loadMessageData = async () => {
		if (message && message?.data === true) {
			const res = await getMessageData(localStorage.token, channel?.id, message.id);
			if (res) {
				message.data = res;
			}
		}
	};

	onMount(async () => {
		if (message && message?.data === true) {
			await loadMessageData();
		}
	});
</script>

<ConfirmDialog
	bind:show={showDeleteConfirmDialog}
	title={$i18n.t('Delete Message')}
	message={$i18n.t('Are you sure you want to delete this message?')}
	onConfirm={async () => {
		await onDelete();
	}}
/>

{#if message}
	<div
		id="message-{message.id}"
		class="flex flex-col justify-between w-full max-w-full mx-auto group hover:bg-gray-300/5 dark:hover:bg-gray-700/5 transition relative {className
			? className
			: `px-5 ${
					replyToMessage ? 'border-l-4 border-blue-500 bg-blue-100/10 dark:bg-blue-100/5 pl-4' : ''
				} ${
					(message?.reply_to_message?.meta?.model_id ?? message?.reply_to_message?.user_id) ===
					$user?.id
						? 'border-l-4 border-orange-500 bg-orange-100/10 dark:bg-orange-100/5 pl-4'
						: ''
				} ${message?.is_pinned ? 'bg-yellow-100/20 dark:bg-yellow-100/5' : ''}`} {showUserProfile
			? 'pt-1.5 pb-0.5'
			: ''}"
	>
		{#if !edit && !disabled}
			<div
				class=" absolute {showButtons ? '' : 'invisible group-hover:visible'} right-1 -top-2 z-10"
			>
				<div
					class="flex gap-1 rounded-lg bg-white dark:bg-gray-850 shadow-md p-0.5 border border-gray-100/30 dark:border-gray-850/30"
				>
					{#if onReaction}
						<EmojiPicker
							onClose={() => (showButtons = false)}
							onSubmit={(name) => {
								showButtons = false;
								onReaction(name);
							}}
						>
							<Tooltip content={$i18n.t('Add Reaction')}>
								<button
									class="hover:bg-gray-100 dark:hover:bg-gray-800 transition rounded-lg p-1"
									on:click={() => {
										showButtons = true;
									}}
								>
									<FaceSmile />
								</button>
							</Tooltip>
						</EmojiPicker>
					{/if}

					{#if onReply}
						<Tooltip content={$i18n.t('Reply')}>
							<button
								class="hover:bg-gray-100 dark:hover:bg-gray-800 transition rounded-lg p-0.5"
								on:click={() => {
									onReply(message);
								}}
							>
								<ArrowUpLeftAlt className="size-5" />
							</button>
						</Tooltip>
					{/if}

					<Tooltip content={message?.is_pinned ? $i18n.t('Unpin') : $i18n.t('Pin')}>
						<button
							class="hover:bg-gray-100 dark:hover:bg-gray-800 transition rounded-lg p-1"
							on:click={() => {
								onPin(message);
							}}
						>
							{#if message?.is_pinned}
								<PinSlash className="size-4" />
							{:else}
								<Pin className="size-4" />
							{/if}
						</button>
					</Tooltip>

					{#if !thread && onThread}
						<Tooltip content={$i18n.t('Reply in Thread')}>
							<button
								class="hover:bg-gray-100 dark:hover:bg-gray-800 transition rounded-lg p-1"
								on:click={() => {
									onThread(message.id);
								}}
							>
								<ChatBubbleOvalEllipsis />
							</button>
						</Tooltip>
					{/if}

					{#if message.user_id === $user?.id || $user?.role === 'admin'}
						{#if onEdit}
							<Tooltip content={$i18n.t('Edit')}>
								<button
									class="hover:bg-gray-100 dark:hover:bg-gray-800 transition rounded-lg p-1"
									on:click={() => {
										edit = true;
										editedContent = message.content;
									}}
								>
									<Pencil />
								</button>
							</Tooltip>
						{/if}

						{#if onDelete}
							<Tooltip content={$i18n.t('Delete')}>
								<button
									class="hover:bg-gray-100 dark:hover:bg-gray-800 transition rounded-lg p-1"
									on:click={() => (showDeleteConfirmDialog = true)}
								>
									<GarbageBin />
								</button>
							</Tooltip>
						{/if}
					{/if}
				</div>
			</div>
		{/if}

		{#if message?.is_pinned}
			<div class="flex {showUserProfile ? 'mb-0.5' : 'mt-0.5'}">
				<div class="ml-8.5 flex items-center gap-1 px-1 rounded-full text-xs">
					<Pin className="size-3 text-yellow-500 dark:text-yellow-300" />
					<span class="text-gray-500">{$i18n.t('Pinned')}</span>
				</div>
			</div>
		{/if}

		{#if message?.reply_to_message?.user}
			<div class="relative text-xs mb-1">
				<div
					class="absolute h-3 w-7 left-[18px] top-2 rounded-tl-lg border-t-[1.5px] border-l-[1.5px] border-gray-200 dark:border-gray-700 z-0"
				></div>

				<button
					class="ml-12 flex items-center space-x-2 relative z-0"
					on:click={() => {
						const messageElement = document.getElementById(
							`message-${message.reply_to_message.id}`
						);
						if (messageElement) {
							messageElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
							messageElement.classList.add('highlight');
							setTimeout(() => {
								messageElement.classList.remove('highlight');
							}, 2000);
							return;
						}
					}}
				>
					{#if message?.reply_to_message?.meta?.model_id}
						<img
							src={`${WEBUI_API_BASE_URL}/models/model/profile/image?id=${message.reply_to_message.meta.model_id}`}
							alt={message.reply_to_message.meta.model_name ??
								message.reply_to_message.meta.model_id}
							class="size-4 ml-0.5 rounded-full object-cover"
							on:error={(e) => {
								e.currentTarget.src = '/favicon.png';
							}}
						/>
					{:else}
						<img
							src={message.reply_to_message.user?.role === 'webhook'
								? `${WEBUI_API_BASE_URL}/channels/webhooks/${message.reply_to_message.user?.id}/profile/image`
								: `${WEBUI_API_BASE_URL}/users/${message.reply_to_message.user?.id}/profile/image`}
							alt={message.reply_to_message.user?.name ?? $i18n.t('Unknown User')}
							class="size-4 ml-0.5 rounded-full object-cover"
						/>
					{/if}

					<div class="shrink-0">
						{message?.reply_to_message.meta?.model_name ??
							message?.reply_to_message.user?.name ??
							$i18n.t('Unknown User')}
					</div>

					<div class="italic text-sm text-gray-500 dark:text-gray-400 line-clamp-1 w-full flex-1">
						<Markdown id={`${message.id}-reply-to`} content={message?.reply_to_message?.content} />
					</div>
				</button>
			</div>
		{/if}

		<div
			class=" flex w-full message-{message.id} "
			id="message-{message.id}"
			dir={$settings.chatDirection}
		>
			<div class={`shrink-0 mr-1 w-9`}>
				{#if showUserProfile}
					{#if message?.meta?.model_id}
						<img
							src={`${WEBUI_API_BASE_URL}/models/model/profile/image?id=${message.meta.model_id}`}
							alt={message.meta.model_name ?? message.meta.model_id}
							class="size-8 translate-y-1 ml-0.5 object-cover rounded-full"
							on:error={(e) => {
								e.currentTarget.src = '/favicon.png';
							}}
						/>
					{:else if message.user?.role === 'webhook'}
						<ProfileImage
							src={`${WEBUI_API_BASE_URL}/channels/webhooks/${message.user?.id}/profile/image`}
							className={'size-8 ml-0.5'}
						/>
					{:else}
						<ProfilePreview user={message.user}>
							<ProfileImage
								src={`${WEBUI_API_BASE_URL}/users/${message.user?.id}/profile/image`}
								className={'size-8 ml-0.5'}
							/>
						</ProfilePreview>
					{/if}
				{:else}
					<!-- <div class="w-7 h-7 rounded-full bg-transparent" /> -->

					{#if message.created_at}
						<div
							class="mt-1.5 flex shrink-0 items-center text-xs self-center invisible group-hover:visible text-gray-500 font-medium first-letter:capitalize"
						>
							<Tooltip content={dayjs(message.created_at / 1000000).format('LLLL')}>
								{dayjs(message.created_at / 1000000).format('HH:mm')}
							</Tooltip>
						</div>
					{/if}
				{/if}
			</div>

			<div class="flex-auto w-0 pl-2">
				{#if showUserProfile}
					<Name>
						<div class=" self-end text-base shrink-0 font-medium truncate">
							{#if message?.meta?.model_id}
								{message?.meta?.model_name ?? message?.meta?.model_id}
							{:else}
								{message?.user?.name}
							{/if}
						</div>

						{#if message.created_at}
							<div
								class=" self-center text-xs text-gray-400 font-medium first-letter:capitalize ml-0.5 translate-y-[1px]"
							>
								<Tooltip content={dayjs(message.created_at / 1000000).format('LLLL')}>
									<span class="line-clamp-1">
										{#if dayjs(message.created_at / 1000000).isToday()}
											{dayjs(message.created_at / 1000000).format('LT')}
										{:else}
											{$i18n.t(formatDate(message.created_at / 1000000), {
												LOCALIZED_TIME: dayjs(message.created_at / 1000000).format('LT'),
												LOCALIZED_DATE: dayjs(message.created_at / 1000000).format('L')
											})}
										{/if}
									</span>
								</Tooltip>
							</div>
						{/if}
					</Name>
				{/if}

				{#if message?.data === true}
					<!-- loading indicator -->
					<div class=" my-2">
						<Skeleton />
					</div>
				{:else if (message?.data?.files ?? []).length > 0}
					<div
						class="my-2.5 w-full flex overflow-x-auto gap-2 flex-wrap"
						dir={$settings?.chatDirection ?? 'auto'}
					>
						{#each message?.data?.files as file}
							{@const fileUrl =
								file.url.startsWith('data') || file.url.startsWith('http')
									? file.url
									: `${WEBUI_API_BASE_URL}/files/${file.url}${file?.content_type ? '/content' : ''}`}
							<div>
								{#if file.type === 'image' || (file?.content_type ?? '').startsWith('image/')}
									<Image src={fileUrl} alt={file.name} imageClassName=" max-h-96 rounded-lg" />
								{:else if file.type === 'video' || (file?.content_type ?? '').startsWith('video/')}
									<video src={fileUrl} controls class=" max-h-96 rounded-lg"></video>
								{:else}
									<FileItem
										item={file}
										url={file.url}
										name={file.name}
										type={file.type}
										size={file?.size}
										small={true}
									/>
								{/if}
							</div>
						{/each}
					</div>
				{/if}

				{#if edit}
					<div class="py-2">
						<Textarea
							className=" bg-transparent outline-hidden w-full resize-none"
							bind:value={editedContent}
							onKeydown={(e) => {
								if (e.key === 'Escape') {
									document.getElementById('close-edit-message-button')?.click();
								}

								const isCmdOrCtrlPressed = e.metaKey || e.ctrlKey;
								const isEnterPressed = e.key === 'Enter';

								if (isCmdOrCtrlPressed && isEnterPressed) {
									document.getElementById('confirm-edit-message-button')?.click();
								}
							}}
						/>
						<div class=" mt-2 mb-1 flex justify-end text-sm font-medium">
							<div class="flex space-x-1.5">
								<button
									id="close-edit-message-button"
									class="px-3.5 py-1.5 bg-white dark:bg-gray-900 hover:bg-gray-100 text-gray-800 dark:text-gray-100 transition rounded-3xl"
									on:click={() => {
										edit = false;
										editedContent = null;
									}}
								>
									{$i18n.t('Cancel')}
								</button>

								<button
									id="confirm-edit-message-button"
									class="px-3.5 py-1.5 bg-gray-900 dark:bg-white hover:bg-gray-850 text-gray-100 dark:text-gray-800 transition rounded-3xl"
									on:click={async () => {
										onEdit(editedContent);
										edit = false;
										editedContent = null;
									}}
								>
									{$i18n.t('Save')}
								</button>
							</div>
						</div>
					</div>
				{:else}
					<div class=" min-w-full markdown-prose {pending ? 'opacity-50' : ''}">
						{#if (message?.content ?? '').trim() === '' && message?.meta?.model_id}
							<Skeleton />
						{:else}
							<Markdown
								id={message.id}
								content={message.content}
								paragraphTag="span"
							/>{#if message.created_at !== message.updated_at && (message?.meta?.model_id ?? null) === null}<span
									class="text-gray-500 text-[10px] pl-1 self-center">({$i18n.t('edited')})</span
								>{/if}
						{/if}
					</div>

					{#if (message?.reactions ?? []).length > 0}
						<div>
							<div class="flex items-center flex-wrap gap-y-1.5 gap-1 mt-1 mb-2">
								{#each message.reactions as reaction}
									<Tooltip
										content={$i18n.t('{{NAMES}} reacted with {{REACTION}}', {
											NAMES: reaction.users
												.reduce((acc, u, idx) => {
													const name = u.id === $user?.id ? $i18n.t('You') : u.name;
													const total = reaction.users.length;

													// First three names always added normally
													if (idx < 3) {
														const separator =
															idx === 0
																? ''
																: idx === Math.min(2, total - 1)
																	? ` ${$i18n.t('and')} `
																	: ', ';
														return `${acc}${separator}${name}`;
													}

													// More than 4 → "and X others"
													if (idx === 3 && total > 4) {
														return (
															acc +
															` ${$i18n.t('and {{COUNT}} others', {
																COUNT: total - 3
															})}`
														);
													}

													return acc;
												}, '')
												.trim(),
											REACTION: `:${reaction.name}:`
										})}
									>
										<button
											class="flex items-center gap-1.5 transition rounded-xl px-2 py-1 cursor-pointer {reaction.users
												.map((u) => u.id)
												.includes($user?.id)
												? ' bg-blue-300/10 outline outline-blue-500/50 outline-1'
												: 'bg-gray-300/10 dark:bg-gray-500/10 hover:outline hover:outline-gray-700/30 dark:hover:outline-gray-300/30 hover:outline-1'}"
											on:click={() => {
												if (onReaction) {
													onReaction(reaction.name);
												}
											}}
										>
											<Emoji shortCode={reaction.name} />

											{#if reaction.users.length > 0}
												<div class="text-xs font-medium text-gray-500 dark:text-gray-400">
													{reaction.users?.length}
												</div>
											{/if}
										</button>
									</Tooltip>
								{/each}

								{#if onReaction}
									<EmojiPicker
										onSubmit={(name) => {
											onReaction(name);
										}}
									>
										<Tooltip content={$i18n.t('Add Reaction')}>
											<div
												class="flex items-center gap-1.5 bg-gray-500/10 hover:outline hover:outline-gray-700/30 dark:hover:outline-gray-300/30 hover:outline-1 transition rounded-xl px-1 py-1 cursor-pointer text-gray-500 dark:text-gray-400"
											>
												<FaceSmile />
											</div>
										</Tooltip>
									</EmojiPicker>
								{/if}
							</div>
						</div>
					{/if}

					{#if !thread && message.reply_count > 0}
						<div class="flex items-center gap-1.5 -mt-0.5 mb-1.5">
							<button
								class="flex items-center text-xs py-1 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 transition"
								on:click={() => {
									onThread(message.id);
								}}
							>
								<span class="font-medium mr-1">
									{$i18n.t('{{COUNT}} Replies', { COUNT: message.reply_count })}</span
								><span>
									{' - '}{$i18n.t('Last reply')}
									{dayjs.unix(message.latest_reply_at / 1000000000).fromNow()}</span
								>

								<span class="ml-1">
									<ChevronRight className="size-2.5" strokeWidth="3" />
								</span>
								<!-- {$i18n.t('View Replies')} -->
							</button>
						</div>
					{/if}
				{/if}
			</div>
		</div>
	</div>
{/if}

<style>
	.highlight {
		animation: highlightAnimation 2s ease-in-out;
	}

	@keyframes highlightAnimation {
		0% {
			background-color: rgba(0, 60, 255, 0.1);
		}
		100% {
			background-color: transparent;
		}
	}
</style>
