<script lang="ts">
	import { untrack, type Component } from "svelte";
	import type { SvelteHTMLElements } from "svelte/elements";
	import { now as tidNow } from "@atcute/tid";
	import X from "$icon/x";
	import { putComment } from "$lib/api/comment";
	import { getAuth } from "$lib/auth.svelte";
	import Button from "$lib/components/ui/Button.svelte";
	import ErrorAlert from "$lib/components/ui/Error.svelte";
	import MarkdownEditor from "$lib/components/ui/MarkdownEditor.svelte";
	import Spinner from "$lib/components/ui/Spinner.svelte";
	import { renderMarkup, type MarkupContext } from "$lib/markup";
	import type { CommentRecord } from "$lib/api/records";
	import type { ThreadInput } from "./comments";

	interface Props {
		subjectUri: string;
		subjectCid?: string;
		// when set, the comment strongRefs this parent comment (threaded reply)
		replyToUri?: string;
		replyToCid?: string;
		authorDid: string;
		authorHandle: string;
		// reused on edit so the record keeps its identity; omitted when composing
		rkey?: string;
		createdAt?: string;
		body?: string;
		markup: MarkupContext;
		rows?: number;
		placeholder?: string;
		previewClass?: string;
		submitLabel: string;
		submitIcon: Component<SvelteHTMLElements["svg"]>;
		autofocus?: boolean;
		onsubmitted?: (submitted: ThreadInput) => void;
		oncancel?: () => void;
	}

	let {
		subjectUri,
		subjectCid,
		replyToUri,
		replyToCid,
		authorDid,
		authorHandle,
		rkey,
		createdAt,
		body: initialBody = "",
		markup,
		rows = 6,
		placeholder,
		previewClass = "min-h-24",
		submitLabel,
		submitIcon,
		autofocus = false,
		onsubmitted,
		oncancel
	}: Props = $props();

	const auth = getAuth();

	let body = $state(untrack(() => initialBody));
	let tab = $state<"write" | "preview">("write");
	let isPublishing = $state(false);
	let error = $state<string | null>(null);

	const canSubmit = $derived(body.trim() !== "" && !isPublishing);

	const handleSubmit = async (e: SubmitEvent) => {
		e.preventDefault();
		const agent = auth.agent;
		if (!agent || !canSubmit) return;
		if (!subjectCid) {
			error = "Cannot comment: the subject record is missing a cid.";
			return;
		}
		isPublishing = true;
		error = null;
		try {
			const createdAtValue = createdAt ?? new Date().toISOString();
			const targetRkey = rkey ?? tidNow();
			const record: CommentRecord = {
				$type: "sh.tangled.feed.comment",
				subject: { uri: subjectUri, cid: subjectCid } as CommentRecord["subject"],
				body: { $type: "sh.tangled.markup.markdown", text: body },
				createdAt: createdAtValue
			};
			if (replyToUri && replyToCid) {
				record.replyTo = { uri: replyToUri, cid: replyToCid } as CommentRecord["replyTo"];
			}
			const saved = await putComment(agent, targetRkey, record);
			// render markdown client-side so the optimistic comment matches a real one
			const bodyHtml = await renderMarkup(body, markup).catch(() => null);
			onsubmitted?.({
				comment: {
					uri: saved.uri,
					cid: saved.cid,
					rkey: targetRkey,
					authorDid,
					authorHandle,
					createdAt: createdAtValue,
					body,
					bodyHtml
				},
				replyTo: replyToUri ?? null
			});
			// keep the prefilled body when editing; reset when composing a fresh comment
			if (!rkey) {
				body = "";
				tab = "write";
			}
		} catch (err) {
			error = err instanceof Error ? err.message : "Failed to post comment";
		} finally {
			isPublishing = false;
		}
	};
</script>

<form onsubmit={handleSubmit} class="flex flex-col gap-2">
	<MarkdownEditor
		name="body"
		{rows}
		{previewClass}
		{placeholder}
		{markup}
		{autofocus}
		transparent
		bind:value={body}
		bind:tab
		disabled={isPublishing}
	/>

	{#if error}
		<ErrorAlert label={error} />
	{/if}

	<div class="flex items-center gap-2">
		<Button
			type="submit"
			variant="primary"
			icon={isPublishing ? Spinner : submitIcon}
			disabled={!canSubmit}
		>
			{submitLabel}
		</Button>
		{#if oncancel}
			<Button type="button" variant="default" icon={X} disabled={isPublishing} onclick={oncancel}>
				Cancel
			</Button>
		{/if}
	</div>
</form>
