<script lang="ts">
	import IssueHeader from "$lib/components/repo/issues/IssueHeader.svelte";
	import IssueInfoBar from "$lib/components/repo/issues/IssueInfoBar.svelte";
	import IssueBody from "$lib/components/repo/issues/IssueBody.svelte";
	import IssueLabelPanel from "$lib/components/repo/issues/IssueLabelPanel.svelte";
	import CommentList from "$lib/components/comment/CommentList.svelte";
	import CommentBox from "$lib/components/comment/CommentBox.svelte";
	import Reactions from "$lib/components/reaction/Reactions.svelte";
	import SignupPrompt from "$lib/components/ui/SignupPrompt.svelte";
	import TabPanel from "$lib/components/ui/TabPanel.svelte";
	import IssueForm from "$lib/components/repo/issues/IssueForm.svelte";
	import ErrorAlert from "$lib/components/ui/Error.svelte";
	import Pencil from "$icon/pencil";
	import Trash2 from "$icon/trash-2";
	import { goto } from "$app/navigation";
	import { resolve } from "$app/paths";
	import { renderMarkup } from "$lib/markup";
	import { deleteIssue } from "$lib/api/issue";
	import type { IssueRecord, RecordView } from "$lib/api/records";
	import type { ThreadInput } from "$lib/components/comment/comments";
	import { getAuth } from "$lib/auth.svelte";
	import { createAction } from "$lib/action.svelte";

	let { data } = $props();

	const auth = getAuth();

	let issue = $derived(data.issue);

	const isAuthor = $derived(auth.currentUser?.did === issue.authorDid);
	const issuesBase = $derived(`/${data.repo.ownerHandle}/${data.repo.name}/issues`);

	let editing = $state(false);

	let comments = $derived(data.comments);

	// optimistic update
	const insertComment = ({ comment, replyTo }: ThreadInput) => {
		if (replyTo === null) {
			comments = [...comments, { self: comment, replies: [] }];
		} else {
			comments = comments.map((thread) =>
				thread.self.uri === replyTo ? { ...thread, replies: [...thread.replies, comment] } : thread
			);
		}
	};

	const updateComment = ({ comment }: ThreadInput) => {
		comments = comments.map((thread) => ({
			self: thread.self.uri === comment.uri ? comment : thread.self,
			replies: thread.replies.map((reply) => (reply.uri === comment.uri ? comment : reply))
		}));
	};

	const removeComment = (uri: string) => {
		comments = comments
			.map((thread) => {
				// a deleted parent that still has replies stays as a tombstone so its
				// replies remain grouped; otherwise drop it entirely
				if (thread.self.uri === uri) {
					return thread.replies.length
						? { ...thread, self: { ...thread.self, body: "", bodyHtml: null, deleted: true } }
						: null;
				}
				return { ...thread, replies: thread.replies.filter((reply) => reply.uri !== uri) };
			})
			.filter((thread) => thread !== null);
	};

	const handleSaved = async (saved: RecordView<IssueRecord>) => {
		const { title, body = "" } = saved.value;
		const bodyHtml = body ? await renderMarkup(body, data.markup).catch(() => null) : null;
		issue = { ...issue, title, body, bodyHtml };
		editing = false;
	};

	const removeIssue = createAction(async () => {
		const agent = auth.agent;
		if (!agent) return;
		await deleteIssue(agent, issue.rkey);
		await goto(resolve(issuesBase as "/"));
	});

	const handleDelete = () => {
		if (!auth.agent || removeIssue.loading) return;
		if (!confirm("Delete this issue? This cannot be undone.")) return;
		void removeIssue.run();
	};
</script>

<svelte:head>
	<title
		>{issue.title} &middot; Issue #{issue.rkey} &middot; {data.repo.ownerHandle}/{data.repo.name}
		&middot; Tangled</title
	>
</svelte:head>

{#snippet issueActions()}
	<button
		type="button"
		aria-label="Edit issue"
		class="cursor-pointer text-foreground-subtle hover:text-foreground-default"
		onclick={() => (editing = true)}
	>
		<Pencil class="size-3" />
	</button>
	<button
		type="button"
		aria-label="Delete issue"
		disabled={removeIssue.loading}
		class="cursor-pointer text-foreground-danger hover:text-foreground-danger-strong disabled:opacity-50"
		onclick={handleDelete}
	>
		<Trash2 class="size-3" />
	</button>
{/snippet}

<div class="grid grid-cols-1 gap-4 lg:grid-cols-10">
	<!-- conversation -->
	<div class="lg:col-span-8">
		<TabPanel>
			{#if editing}
				<IssueForm
					mode="edit"
					repoDid={data.repo.repoDid ?? ""}
					markup={data.markup}
					rkey={issue.rkey}
					createdAt={issue.createdAt}
					title={issue.title}
					body={issue.body}
					onsaved={handleSaved}
					oncancel={() => (editing = false)}
				/>
			{:else}
				<IssueHeader title={issue.title} rkey={issue.rkey} />
				<IssueInfoBar
					state={issue.state}
					authorHandle={issue.authorHandle}
					authorDid={issue.authorDid}
					createdAt={issue.createdAt}
					actions={isAuthor ? issueActions : undefined}
				/>
				<IssueBody body={issue.body} bodyHtml={issue.bodyHtml} />
				<Reactions reactions={issue.reactions} subjectUri={issue.uri} alwaysShow class="mt-2" />
				{#if removeIssue.error}
					<div class="mt-3">
						<ErrorAlert label={removeIssue.error} />
					</div>
				{/if}
			{/if}
		</TabPanel>

		<div class="mt-4 flex flex-col gap-4">
			<CommentList
				threads={comments}
				subjectUri={issue.uri}
				subjectCid={issue.cid}
				markup={data.markup}
				onsubmitted={insertComment}
				onedited={updateComment}
				ondeleted={removeComment}
			/>

			{#if auth.currentUser}
				<CommentBox
					subjectUri={issue.uri}
					subjectCid={issue.cid}
					authorDid={auth.currentUser.did}
					authorHandle={auth.currentUser.handle}
					markup={data.markup}
					onsubmitted={insertComment}
				/>
			{:else}
				<SignupPrompt />
			{/if}
		</div>
	</div>

	<!-- label panel -->
	<div class="lg:col-span-2">
		<IssueLabelPanel />
	</div>
</div>
