<script lang="ts">
	import type { AtpClient } from '$lib/at/client';
	import { ok, err, type Result, expect } from '$lib/result';
	import type { AppBskyFeedPost } from '@atcute/bluesky';
	import { generateColorForDid } from '$lib/accounts';
	import type { PostWithUri } from '$lib/at/fetch';
	import BskyPost from './BskyPost.svelte';
	import { parseCanonicalResourceUri, type Did } from '@atcute/lexicons';
	import type { ComAtprotoRepoStrongRef } from '@atcute/atproto';
	import type { Writable } from 'svelte/store';

	interface Props {
		client: AtpClient;
		selectedDid: Writable<Did | null>;
		onPostSent: (post: PostWithUri) => void;
		quoting?: PostWithUri;
		replying?: PostWithUri;
	}

	let {
		client,
		selectedDid,
		onPostSent,
		quoting = $bindable(undefined),
		replying = $bindable(undefined)
	}: Props = $props();

	let color = $derived(
		client.didDoc?.did ? generateColorForDid(client.didDoc?.did) : 'var(--nucleus-accent)'
	);

	const post = async (text: string): Promise<Result<PostWithUri, string>> => {
		const strongRef = (p: PostWithUri): ComAtprotoRepoStrongRef.Main => ({
			$type: 'com.atproto.repo.strongRef',
			cid: p.cid!,
			uri: p.uri
		});
		const record: AppBskyFeedPost.Main = {
			$type: 'app.bsky.feed.post',
			text,
			reply: replying
				? {
						root: replying.record.reply?.root ?? strongRef(replying),
						parent: strongRef(replying)
					}
				: undefined,
			embed: quoting
				? {
						$type: 'app.bsky.embed.record',
						record: strongRef(quoting)
					}
				: undefined,
			createdAt: new Date().toISOString()
		};

		const res = await client.atcute?.post('com.atproto.repo.createRecord', {
			input: {
				collection: 'app.bsky.feed.post',
				repo: client.didDoc!.did,
				record
			}
		});

		if (!res) {
			return err('failed to post: not logged in');
		}

		if (!res.ok) {
			return err(`failed to post: ${res.data.error}: ${res.data.message ?? 'no details'}`);
		}

		return ok({
			uri: res.data.uri,
			cid: res.data.cid,
			record
		});
	};

	let postText = $state('');
	let info = $state('');
	let isFocused = $state(false);
	let textareaEl: HTMLTextAreaElement | undefined = $state();

	const unfocus = () => {
		isFocused = false;
		quoting = undefined;
		replying = undefined;
	};

	const doPost = () => {
		if (postText.length === 0 || postText.length > 300) return;

		post(postText).then((res) => {
			if (res.ok) {
				onPostSent(res.value);
				postText = '';
				info = 'posted!';
				unfocus();
				setTimeout(() => (info = ''), 1000 * 0.8);
			} else {
				// todo: add a way to clear error
				info = res.error;
			}
		});
	};

	$effect(() => {
		if (isFocused && textareaEl) textareaEl.focus();
		if (quoting || replying) isFocused = true;
	});
</script>

<div class="relative min-h-16">
	<!-- Spacer to maintain layout when focused -->
	{#if isFocused}
		<div class="min-h-16"></div>
	{/if}

	<!-- svelte-ignore a11y_no_static_element_interactions -->
	<div
		onmousedown={(e) => {
			if (isFocused) {
				e.preventDefault();
			}
		}}
		class="flex max-w-full rounded-sm border-2 shadow-lg transition-all duration-300"
		class:min-h-16={!isFocused}
		class:items-center={!isFocused}
		class:shadow-2xl={isFocused}
		class:absolute={isFocused}
		class:top-0={isFocused}
		class:left-0={isFocused}
		class:right-0={isFocused}
		class:z-50={isFocused}
		style="background: {isFocused
			? `color-mix(in srgb, var(--nucleus-bg) 80%, ${color} 20%)`
			: `color-mix(in srgb, ${color} 9%, transparent)`};
			border-color: color-mix(in srgb, {color} {isFocused ? '100' : '40'}%, transparent);"
	>
		<div class="w-full p-2" class:py-3={isFocused}>
			{#if info.length > 0}
				<div
					class="rounded-sm px-3 py-1.5 text-center font-medium text-nowrap overflow-ellipsis"
					style="background: color-mix(in srgb, {color} 13%, transparent); color: {color};"
				>
					{info}
				</div>
			{:else}
				<div class="flex flex-col gap-2">
					{#if isFocused}
						{#if replying}
							{@const parsedUri = expect(parseCanonicalResourceUri(replying.uri))}
							<BskyPost
								{client}
								{selectedDid}
								did={parsedUri.repo}
								rkey={parsedUri.rkey}
								data={replying}
								isOnPostComposer={true}
							/>
						{/if}
						<textarea
							bind:this={textareaEl}
							bind:value={postText}
							onfocus={() => (isFocused = true)}
							onblur={unfocus}
							onkeydown={(event) => {
								if (event.key === 'Escape') unfocus();
								if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) doPost();
							}}
							placeholder="what's on your mind?"
							rows="4"
							class="[field-sizing:content] single-line-input resize-none bg-(--nucleus-bg)/40 focus:scale-100"
							style="border-color: color-mix(in srgb, {color} 27%, transparent);"
						></textarea>
						{#if quoting}
							{@const parsedUri = expect(parseCanonicalResourceUri(quoting.uri))}
							<BskyPost
								{client}
								{selectedDid}
								did={parsedUri.repo}
								rkey={parsedUri.rkey}
								data={quoting}
								isOnPostComposer={true}
							/>
						{/if}
						<div class="flex items-center gap-2">
							<div class="grow"></div>
							<span
								class="text-sm font-medium"
								style="color: color-mix(in srgb, {postText.length > 300
									? '#ef4444'
									: 'var(--nucleus-fg)'} 53%, transparent);"
							>
								{postText.length} / 300
							</span>
							<button
								onmousedown={(e) => {
									e.preventDefault();
									doPost();
								}}
								disabled={postText.length === 0 || postText.length > 300}
								class="action-button border-none px-5 text-(--nucleus-fg)/94 disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:scale-100"
								style="background: color-mix(in srgb, {color} 87%, transparent);"
							>
								post
							</button>
						</div>
					{:else}
						<input
							bind:value={postText}
							onfocus={() => (isFocused = true)}
							type="text"
							placeholder="what's on your mind?"
							class="single-line-input flex-1 bg-(--nucleus-bg)/40"
							style="border-color: color-mix(in srgb, {color} 27%, transparent);"
						/>
					{/if}
				</div>
			{/if}
		</div>
	</div>
</div>
