<script lang="ts">
	import { getContext, onDestroy, onMount } from "svelte";
	const i18n = getContext("i18n");

	import { channels, models, user } from "$lib/stores";
	import Tooltip from "$lib/components/common/Tooltip.svelte";
	import Hashtag from "$lib/components/icons/Hashtag.svelte";
	import Lock from "$lib/components/icons/Lock.svelte";
	import { REXPRO_API_BASE_URL, REXPRO_BASE_URL } from "$lib/constants";
	import { searchUsers } from "$lib/apis/users";

	export let query = "";

	export let command: (payload: { id: string; label: string }) => void;
	export let selectedIndex = 0;

	export let label = "";
	export let triggerChar = "@";

	export let modelSuggestions = false;
	export let userSuggestions = false;
	export let channelSuggestions = false;

	let _models = [];
	let _users = [];
	let _channels = [];

	$: filteredItems = [..._users, ..._models, ..._channels].filter(
		(u) =>
			u.label.toLowerCase().includes(query.toLowerCase()) ||
			u.id.toLowerCase().includes(query.toLowerCase()),
	);

	const getUserList = async () => {
		const res = await searchUsers(localStorage.token, query).catch(
			(error) => {
				console.error("Error searching users:", error);
				return null;
			},
		);

		if (res) {
			_users = [
				...res.users.map((u) => ({
					type: "user",
					id: u.id,
					label: u.name,
				})),
			].sort((a, b) => a.label.localeCompare(b.label));
		}
	};

	$: if (query !== null && userSuggestions) {
		getUserList();
	}

	const select = (index: number) => {
		const item = filteredItems[index];
		if (!item) return;

		// Add the "U:", "M:" or "C:" prefix to the id
		// and also append the label after a pipe |
		// so that the mention renderer can show the label
		if (item)
			command({
				id: `${item.type === "user" ? "U" : item.type === "model" ? "M" : "C"}:${item.id}|${item.label}`,
				label: item.label,
			});
	};

	const onKeyDown = (event: KeyboardEvent) => {
		if (
			!["ArrowUp", "ArrowDown", "Enter", "Tab", "Escape"].includes(
				event.key,
			)
		)
			return false;

		if (event.key === "ArrowUp") {
			selectedIndex = Math.max(0, selectedIndex - 1);
			const item = document.querySelector(`[data-selected="true"]`);
			item?.scrollIntoView({
				block: "center",
				inline: "nearest",
				behavior: "instant",
			});
			return true;
		}
		if (event.key === "ArrowDown") {
			selectedIndex = Math.min(
				selectedIndex + 1,
				filteredItems.length - 1,
			);
			const item = document.querySelector(`[data-selected="true"]`);
			item?.scrollIntoView({
				block: "center",
				inline: "nearest",
				behavior: "instant",
			});
			return true;
		}
		if (event.key === "Enter" || event.key === "Tab") {
			select(selectedIndex);

			if (event.key === "Enter") {
				event.preventDefault();
			}
			return true;
		}
		if (event.key === "Escape") {
			// tell tiptap we handled it (it will close)
			return true;
		}
		return false;
	};

	// This method will be called from the suggestion renderer
	// @ts-ignore
	export function _onKeyDown(event: KeyboardEvent) {
		return onKeyDown(event);
	}

	onMount(() => {
		const keydownListener = (e: KeyboardEvent) => {
			// required to prevent the default enter behavior
			if (e.key === "Enter") {
				e.preventDefault();
				select(selectedIndex);
			}
		};

		window.addEventListener("keydown", keydownListener);

		if (channelSuggestions) {
			// Add a dummy channel item
			_channels = [
				...$channels
					.filter((c) => c?.type !== "dm")
					.map((c) => ({
						type: "channel",
						id: c.id,
						label: c.name,
						data: c,
					})),
			];
		} else {
			if (userSuggestions) {
				getUserList();
			}

			if (modelSuggestions) {
				_models = [
					...$models
						.filter((m) => !m?.direct)
						.map((m) => ({
							type: "model",
							id: m.id,
							label: m.name,
							data: m,
						})),
				];
			}
		}

		return () => {
			window.removeEventListener("keydown", keydownListener);
		};
	});

	const hasPublicReadGrant = (grants: any) =>
		Array.isArray(grants) &&
		grants.some(
			(grant) =>
				grant?.principal_type === "user" &&
				grant?.principal_id === "*" &&
				grant?.permission === "read",
		);

	const isPublicChannel = (channel: any): boolean => {
		if (channel?.type === "group") {
			if (typeof channel?.is_private === "boolean") {
				return !channel.is_private;
			}
			return hasPublicReadGrant(channel?.access_grants);
		}
		return hasPublicReadGrant(channel?.access_grants);
	};
</script>

{#if filteredItems.length}
	<div
		class="mention-list text-black dark:text-white rounded-2xl shadow-lg border border-gray-200 dark:border-gray-800 flex flex-col bg-white dark:bg-gray-850 w-72 p-1"
		id="suggestions-container"
	>
		<div class="overflow-y-auto scrollbar-thin max-h-60">
			{#each filteredItems as item, i}
				{#if i === 0 || item?.type !== filteredItems[i - 1]?.type}
					<div class="px-2 text-xs text-gray-500 py-1">
						{#if item?.type === "user"}
							{$i18n.t("Users")}
						{:else if item?.type === "model"}
							{$i18n.t("Models")}
						{:else if item?.type === "channel"}
							{$i18n.t("Channels")}
						{/if}
					</div>
				{/if}

				<Tooltip content={item?.id} placement="top-start">
					<button
						type="button"
						on:click={() => select(i)}
						on:mousemove={() => {
							selectedIndex = i;
						}}
						class="flex items-center justify-between px-2.5 py-1.5 rounded-xl w-full text-left {i ===
						selectedIndex
							? 'bg-gray-50 dark:bg-gray-800 selected-command-option-button'
							: ''}"
						data-selected={i === selectedIndex}
					>
						{#if item.type === "channel"}
							<div
								class=" size-4 justify-center flex items-center mr-0.5"
							>
								{#if isPublicChannel(item?.data)}
									<Hashtag
										className="size-3"
										strokeWidth="2.5"
									/>
								{:else}
									<Lock
										className="size-[15px]"
										strokeWidth="2"
									/>
								{/if}
							</div>
						{:else if item.type === "model"}
							<img
								src={`${REXPRO_API_BASE_URL}/models/model/profile/image?id=${item.id}&lang=${$i18n.language}`}
								alt={item?.data?.name ?? item.id}
								class="rounded-full size-5 items-center mr-2"
								on:error={(e) => {
									e.currentTarget.src = "/favicon.png";
								}}
							/>
						{:else if item.type === "user"}
							<img
								src={`${REXPRO_API_BASE_URL}/users/${item.id}/profile/image`}
								alt={item?.label ?? item.id}
								class="rounded-full size-5 items-center mr-2"
								on:error={(e) => {
									e.currentTarget.src = "/favicon.png";
								}}
							/>
						{/if}

						<div class="truncate flex-1 pr-2">
							{item.label}
						</div>

						<div class="shrink-0 text-xs text-gray-500">
							{#if item.type === "user"}
								{$i18n.t("User")}
							{:else if item.type === "model"}
								{$i18n.t("Model")}
							{:else if item.type === "channel"}
								{$i18n.t("Channel")}
							{/if}
						</div>
					</button>
				</Tooltip>
			{/each}
		</div>
	</div>
{/if}
