<script lang="ts">
	import { ListGroup, Button, ListGroupItem } from "@sveltestrap/sveltestrap";
	import type { PageData } from "./$types";
	import { makeFullOptions } from "$lib/util";
	import ChannelSelect from "../ChannelSelect.svelte";
	import apiFetch, {
		fastFetch,
		TOKEN_KEY,
		type ApiError,
		type GuildRole,
	} from "$lib/api";
	import { addToast } from "$lib/toast";
	import RoleSelect from "../RoleSelect.svelte";
	import RoleListItem from "$lib/components/RoleListItem.svelte";
	import RemovableListItem from "$lib/components/RemovableListItem.svelte";
	import Svelecte from "svelecte";

	export let data: PageData;

	$: ignoredChannels = data.guild.messages.ignored_channels;
	$: channelOptions = makeFullOptions(data.guild, ignoredChannels);

	$: ignoredRoles = data.guild.messages.ignored_roles;
	$: roleOptions = data.guild.roles
		.filter((r) => ignoredRoles.indexOf(r.id) === -1)
		.map((r) => ({ label: r.name, value: r.id }));

	$: allChannels = [
		...data.guild.channels_without_category.map((c) => ({
			id: c.id,
			name: `#${c.name}`,
		})),
		...data.guild.categories.map((cat) => ({ id: cat.id, name: cat.name })),
		...data.guild.categories.flatMap((cat) =>
			cat.channels.map((c) => ({ id: c.id, name: `#${c.name}` })),
		),
	];

	const channelName = (id: string) =>
		allChannels.find((c) => c.id === id)?.name || `(unknown channel ${id})`;

	const roleName = (id: string) =>
		data.guild.roles.find((r) => r.id === id)?.name || `(unknown role ${id})`;

	const roleObject = (id: string): GuildRole | string =>
		data.guild.roles.find((r) => r.id === id) || id;

	let channelToIgnore: string | null = null;
	let roleToIgnore: string | null = null;
	let userToIgnore: string | null = null;
	const userIdRegex = /^\d{15,}$/;

	const addChannelIgnore = async () => {
		if (!channelToIgnore) return;

		try {
			await fastFetch(
				"PUT",
				`/api/guilds/${data.guild.id}/ignored-messages/channels/${channelToIgnore}`,
			);
			ignoredChannels.push(channelToIgnore);
			ignoredChannels = ignoredChannels;
			addToast({
				header: "Ignored channel",
				body: `Added ${channelName(channelToIgnore)} to the list of ignored channels.`,
			});
			channelToIgnore = null;
		} catch (e) {
			addToast({
				header: "Error ignoring channel",
				body:
					(e as ApiError).message || "Unknown error. Please try again later.",
			});
		}
	};

	const removeChannelIgnore = async (id: string) => {
		try {
			await fastFetch(
				"DELETE",
				`/api/guilds/${data.guild.id}/ignored-messages/channels/${id}`,
			);
			const idx = ignoredChannels.indexOf(id);
			if (idx > -1) ignoredChannels.splice(idx, 1);
			ignoredChannels = ignoredChannels;
			addToast({
				header: "Stopped ignoring channel",
				body: `Removed ${channelName(id)} from the list of ignored channels.`,
			});
		} catch (e) {
			addToast({
				header: "Error removing channel",
				body:
					(e as ApiError).message || "Unknown error. Please try again later.",
			});
		}
	};

	const addRoleIgnore = async () => {
		if (!roleToIgnore) return;

		try {
			await fastFetch(
				"PUT",
				`/api/guilds/${data.guild.id}/ignored-messages/roles/${roleToIgnore}`,
			);
			ignoredRoles.push(roleToIgnore);
			ignoredRoles = ignoredRoles;
			addToast({
				header: "Ignored role",
				body: `Added ${roleName(roleToIgnore)} to the list of ignored roles.`,
			});
			roleToIgnore = null;
		} catch (e) {
			addToast({
				header: "Error ignoring role",
				body:
					(e as ApiError).message || "Unknown error. Please try again later.",
			});
		}
	};

	const removeRoleIgnore = async (id: string) => {
		try {
			await fastFetch(
				"DELETE",
				`/api/guilds/${data.guild.id}/ignored-messages/roles/${id}`,
			);
			const idx = ignoredRoles.indexOf(id);
			if (idx > -1) ignoredRoles.splice(idx, 1);
			ignoredRoles = ignoredRoles;
			addToast({
				header: "Stopped ignoring role",
				body: `Removed ${roleName(id)} from the list of ignored roles.`,
			});
		} catch (e) {
			addToast({
				header: "Error removing role",
				body:
					(e as ApiError).message || "Unknown error. Please try again later.",
			});
		}
	};

	const addUserIgnore = async () => {
		if (!userToIgnore) return;

		try {
			const user = await apiFetch<{ id: string; tag: string }>(
				"PUT",
				`/api/guilds/${data.guild.id}/ignored-users/${userToIgnore}`,
			);
			data.users.push(user);
			data.users = data.users;
			addToast({
				header: "Ignored user",
				body: `Added ${user.tag} to the list of ignored users.`,
			});
			userToIgnore = null;
		} catch (e) {
			addToast({
				header: "Error ignoring user",
				body:
					(e as ApiError).message || "Unknown error. Please try again later.",
			});
		}
	};

	const removeUserIgnore = async (id: string) => {
		try {
			await fastFetch(
				"DELETE",
				`/api/guilds/${data.guild.id}/ignored-users/${id}`,
			);
			const user = data.users.find((u) => u.id === id);
			const idx = data.users.findIndex((u) => u.id === id);
			if (idx > -1) data.users.splice(idx, 1);
			data.users = data.users;
			addToast({
				header: "Stopped ignoring user",
				body: `Removed ${user?.tag || "unknown user " + id} from the list of ignored users.`,
			});
		} catch (e) {
			addToast({
				header: "Error removing user",
				body:
					(e as ApiError).message || "Unknown error. Please try again later.",
			});
		}
	};

	const fetchProps: RequestInit = {
		method: "GET",
		headers: {
			Authorization: localStorage.getItem(TOKEN_KEY) || "",
		},
	};
</script>

<h3>Ignored messages</h3>

<p>Here you can select which channels and roles to ignore messages from.</p>

<h4>How it works</h4>

<p>Messages will be ignored if:</p>

<ul>
	<li>They are posted in a channel that is listed here</li>
	<li>
		They are posted in a channel in a <em>category</em> that is listed here
	</li>
	<li>They are posted in a thread that's in a channel that is listed here</li>
	<li>
		They are posted by a person with <em>at least one role</em> that is listed here
	</li>
	<li>They are posted by a user that is listed here</li>
</ul>

<p>
	Additionally, messages from <a href="/dash/{data.guild.id}/ignored-users"
		>ignored users</a
	> are also ignored.
</p>

<h4 class="mt-4">Channels</h4>

<div>
	<ChannelSelect bind:value={channelToIgnore} options={channelOptions} />
</div>

<div class="mt-2 mb-3 d-grid d-md-block">
	<Button
		color="primary"
		on:click={() => addChannelIgnore()}
		disabled={!channelToIgnore}
	>
		Ignore messages from this channel
	</Button>
</div>

<ListGroup>
	{#each ignoredChannels as id}
		<RemovableListItem
			on:click={() => removeChannelIgnore(id)}
			buttonText="Stop ignoring"
		>
			{channelName(id)}
		</RemovableListItem>
	{:else}
		<ListGroupItem>
			No channels are having their messages ignored right now.
		</ListGroupItem>
	{/each}
</ListGroup>

<h4 class="mt-4">Roles</h4>

<div>
	<RoleSelect bind:value={roleToIgnore} options={roleOptions} />
</div>

<div class="mt-2 mb-3 d-grid d-md-block">
	<Button
		color="primary"
		on:click={() => addRoleIgnore()}
		disabled={!roleToIgnore}
	>
		Ignore messages from this role
	</Button>
</div>

<ListGroup>
	{#each ignoredRoles as id}
		<RoleListItem
			role={roleObject(id)}
			on:click={() => removeRoleIgnore(id)}
			buttonText="Stop ignoring"
		/>
	{:else}
		<ListGroupItem>
			No roles are having their messages ignored right now.
		</ListGroupItem>
	{/each}
</ListGroup>

<h4 class="mt-4">Users</h4>

<div>
	<Svelecte
		bind:value={userToIgnore}
		fetch="/api/guilds/{data.guild.id}/users?query=[query]"
		{fetchProps}
		multiple={false}
		searchable={true}
		creatable={true}
		labelField="name"
		valueField="id"
		creatablePrefix="user with ID "
	/>
	{#if userToIgnore && !userIdRegex.test(userToIgnore)}
		<p class="text-danger mt-2">
			If you're not ignoring a member of your server, you need to give a
			<strong>user ID</strong>, not their username.
		</p>
	{/if}
</div>

<div class="my-2 d-grid d-md-block">
	<Button
		color="primary"
		on:click={() => addUserIgnore()}
		disabled={!userToIgnore || !userIdRegex.test(userToIgnore)}
	>
		Ignore user
	</Button>
</div>

<ListGroup>
	{#each data.users as user (user.id)}
		<RemovableListItem
			on:click={() => removeUserIgnore(user.id)}
			buttonText="Stop ignoring"
		>
			{user.tag} (ID: {user.id})
		</RemovableListItem>
	{:else}
		<ListGroupItem>No users are being ignored right now.</ListGroupItem>
	{/each}
</ListGroup>
