<!--
Phase 28 — right-pane transcript for a single per-race thread.
Renders messages oldest → newest, with outgoing messages visually
distinct from incoming. Each message body goes through `textContent`
(no HTML parsing); the optional translation has a per-message
"show original" / "show translation" toggle. A persistent reply box
sits at the bottom of the pane.
-->
<script lang="ts">
	import { onMount } from "svelte";

	import { i18n } from "$lib/i18n/index.svelte";
	import { mailStore, type MailThread } from "$lib/mail-store.svelte";
	import type { MailMessage } from "../../../api/diplomail";

	let {
		thread,
		gameId,
	}: {
		thread: MailThread;
		gameId: string;
	} = $props();

	let replyBody = $state("");
	let replyError = $state<string | null>(null);
	let sending = $state(false);
	const showOriginal = $state<Map<string, boolean>>(new Map());

	// Mark every still-unread incoming message in this thread as read
	// when the pane mounts. Idempotent on the server; the store
	// optimistically flips `readAt` so the header badge updates
	// without waiting for the round-trip.
	onMount(() => {
		for (const m of thread.messages) {
			const incoming = m.senderRaceName === thread.raceName;
			if (incoming && m.readAt === null) {
				void mailStore.markRead(m.messageId);
			}
		}
	});

	function isOutgoing(m: MailMessage): boolean {
		// Outgoing messages have the local user as sender, which
		// corresponds to `recipientRaceName === thread.raceName` (the
		// thread is keyed on the other party's race name).
		return m.recipientRaceName === thread.raceName;
	}

	function displayBody(m: MailMessage): string {
		if (m.translatedBody && !showOriginal.get(m.messageId)) {
			return m.translatedBody;
		}
		return m.body;
	}

	function displaySubject(m: MailMessage): string {
		if (m.translatedSubject && !showOriginal.get(m.messageId)) {
			return m.translatedSubject ?? "";
		}
		return m.subject;
	}

	function toggleTranslation(messageId: string): void {
		showOriginal.set(messageId, !(showOriginal.get(messageId) ?? false));
		// Trigger reactivity on the map proxy.
		showOriginal.size; // eslint-disable-line @typescript-eslint/no-unused-expressions
	}

	async function submitReply(event: SubmitEvent): Promise<void> {
		event.preventDefault();
		replyError = null;
		const body = replyBody.trim();
		if (body === "") {
			replyError = i18n.t("game.mail.body_required");
			return;
		}
		sending = true;
		try {
			await mailStore.composePersonal({
				raceName: thread.raceName,
				subject: "",
				body,
			});
			replyBody = "";
		} catch (err) {
			replyError = err instanceof Error ? err.message : String(err);
		} finally {
			sending = false;
		}
	}

	$effect(() => {
		// Force the component to depend on the gameId rune so a game
		// switch re-mounts the pane (the parent unmounts the old one
		// implicitly via the entry-key change, but this keeps the
		// dependency explicit for SSR-disabled hot reloads).
		void gameId;
	});
</script>

<div class="thread" data-testid="mail-thread-pane">
	<h3 class="title">{thread.raceName}</h3>
	<ol class="messages">
		{#each thread.messages as m (m.messageId)}
			<li class="message" class:outgoing={isOutgoing(m)}>
				<div class="meta">
					<span class="from">
						{#if isOutgoing(m)}
							{i18n.t("game.mail.outgoing_label")}
						{:else}
							{thread.raceName}
						{/if}
					</span>
					<time>{m.createdAt.toISOString().slice(0, 19).replace("T", " ")}</time>
				</div>
				{#if displaySubject(m)}
					<div class="subject">{displaySubject(m)}</div>
				{/if}
				<p class="body">{displayBody(m)}</p>
				{#if m.translatedBody}
					<button
						type="button"
						class="toggle"
						onclick={() => toggleTranslation(m.messageId)}
					>
						{#if showOriginal.get(m.messageId)}
							{i18n.t("game.mail.show_translation")}
						{:else}
							{i18n.t("game.mail.show_original")}
						{/if}
					</button>
				{/if}
				{#if !isOutgoing(m)}
					<button
						type="button"
						class="delete"
						onclick={() => mailStore.softDelete(m.messageId)}
						data-testid="mail-delete"
					>
						{i18n.t("game.mail.delete_action")}
					</button>
				{/if}
			</li>
		{/each}
	</ol>

	<form class="reply" onsubmit={submitReply}>
		<label for="mail-reply-body">{i18n.t("game.mail.reply_label")}</label>
		<textarea
			id="mail-reply-body"
			bind:value={replyBody}
			placeholder={i18n.t("game.mail.body_placeholder")}
			rows="3"
			data-testid="mail-reply-body"
		></textarea>
		{#if replyError}
			<p class="error" data-testid="mail-reply-error">{replyError}</p>
		{/if}
		<button type="submit" disabled={sending} data-testid="mail-reply-send">
			{i18n.t("game.mail.compose.send")}
		</button>
	</form>
</div>

<style>
	.thread {
		display: flex;
		flex-direction: column;
		gap: 0.75rem;
		height: 100%;
	}
	.title {
		margin: 0;
		font-size: 1rem;
	}
	.messages {
		list-style: none;
		margin: 0;
		padding: 0;
		display: flex;
		flex-direction: column;
		gap: 0.5rem;
		overflow-y: auto;
		max-height: 50vh;
	}
	.message {
		padding: 0.5rem 0.75rem;
		border-radius: 6px;
		background: var(--color-surface-raised);
		border: 1px solid var(--color-border-subtle);
	}
	.message.outgoing {
		background: var(--color-accent-subtle);
	}
	.meta {
		display: flex;
		justify-content: space-between;
		font-size: 0.8rem;
		color: var(--color-text-muted);
		margin-bottom: 0.25rem;
	}
	.subject {
		font-weight: 700;
	}
	.body {
		margin: 0.25rem 0 0;
		white-space: pre-wrap;
	}
	.toggle,
	.delete {
		margin-top: 0.5rem;
		margin-right: 0.5rem;
		font: inherit;
		padding: 0.2rem 0.5rem;
		border: 1px solid var(--color-border);
		background: transparent;
		color: inherit;
		border-radius: 4px;
		cursor: pointer;
		font-size: 0.8rem;
	}
	.reply {
		display: flex;
		flex-direction: column;
		gap: 0.25rem;
	}
	.reply textarea {
		font: inherit;
		padding: 0.5rem;
		border: 1px solid var(--color-border);
		background: var(--color-surface);
		color: inherit;
		border-radius: 4px;
		resize: vertical;
	}
	.reply button {
		align-self: flex-end;
		font: inherit;
		padding: 0.35rem 0.75rem;
		border: 1px solid var(--color-border);
		background: var(--color-surface-raised);
		color: var(--color-text);
		border-radius: 4px;
		cursor: pointer;
	}
	.reply button:disabled {
		opacity: 0.5;
		cursor: not-allowed;
	}
	.error {
		color: var(--color-danger);
		font-size: 0.85rem;
		margin: 0;
	}
</style>
