<script lang="ts">
	import { onMount } from 'svelte';
	import { goto } from '$app/navigation';
	import { getSupabase } from '$lib/supabase';
	import { currentUser } from '$lib/stores/auth';
	import { userCollectives, currentCollective } from '$lib/stores/collective';
	import { logout } from '$lib/auth';
	import Avatar from '$lib/components/Avatar.svelte';
	import ImageCropper from '$lib/components/ImageCropper.svelte';
	import ThemeToggle from '$lib/components/ThemeToggle.svelte';
	import SyncConflictsPanel from '$lib/components/SyncConflictsPanel.svelte';
	import TagChip from '$lib/components/TagChip.svelte';
	import {
		tags as tagsStore,
		loadTags,
		renameTag,
		recolorTag,
		deleteTag
	} from '$lib/stores/tags';
	import type { ItemTag, ItemTagColor } from '@colectivo/types';
	import { languageTag, setLanguageTag } from '$lib/paraglide/runtime';
	import * as m from '$lib/paraglide/messages';
	import type { ThemePreference } from '$lib/theme';
	import {
		PUBLIC_KEYCLOAK_URL,
		PUBLIC_KEYCLOAK_REALM
	} from '$env/static/public';

	// Profile state loaded from public.users
	let displayNameInput = $state('');
	let language = $state<'en' | 'es'>('en');
	let avatarType = $state<'initials' | 'emoji' | 'upload'>('initials');
	let avatarEmoji = $state<string | null>(null);
	let avatarUrl = $state<string | null>(null);

	let saving = $state(false);
	let saved = $state(false);
	let debounceTimer: ReturnType<typeof setTimeout>;

	// Cropper
	let cropperFile = $state<File | null>(null);
	let uploading = $state(false);

	const EMOJI_GRID = [
		'😀','😎','🥳','🤓','👻','🐱','🐶','🦊',
		'🐸','🦁','🐻','🐼','🐨','🐯','🦄','🐧',
		'🦋','🌈','⭐','🌙','☀️','🌊','🌴','🌺',
		'🍕','🍦','🎸','🎯','🚀','⚽','🎮','🎨'
	];

	const keycloakAccountUrl = `${PUBLIC_KEYCLOAK_URL}/realms/${PUBLIC_KEYCLOAK_REALM}/account`;

	onMount(async () => {
		await loadProfile();
		if ($currentCollective) {
			await loadTags($currentCollective.id);
		}
	});

	// Inline edit state for the tag list (one row at a time).
	let editingTagId = $state<string | null>(null);
	let editingTagName = $state('');
	const TAG_COLORS: ItemTagColor[] = [
		'slate',
		'red',
		'amber',
		'green',
		'sky',
		'indigo',
		'pink',
		'stone'
	];

	function startEditTag(tag: ItemTag) {
		editingTagId = tag.id;
		editingTagName = tag.name;
	}

	async function commitEditTag() {
		const id = editingTagId;
		const next = editingTagName.trim();
		editingTagId = null;
		editingTagName = '';
		if (id && next) {
			await renameTag(id, next);
		}
	}

	function cancelEditTag() {
		editingTagId = null;
		editingTagName = '';
	}

	async function handleRecolor(tag: ItemTag, color: ItemTagColor) {
		if (tag.color === color) return;
		await recolorTag(tag.id, color);
	}

	async function handleDeleteTag(id: string) {
		await deleteTag(id);
	}

	async function loadProfile() {
		if (!$currentUser) return;

		const { data } = await getSupabase()
			.from('users')
			.select('display_name, language, avatar_type, avatar_emoji, avatar_url')
			.eq('id', $currentUser.id)
			.single();

		if (data) {
			displayNameInput = data.display_name;
			language = data.language as 'en' | 'es';
			avatarType = data.avatar_type as 'initials' | 'emoji' | 'upload';
			avatarEmoji = data.avatar_emoji;
			avatarUrl = data.avatar_url;
		}
	}

	function scheduleNameSave() {
		clearTimeout(debounceTimer);
		debounceTimer = setTimeout(saveName, 700);
	}

	async function saveName() {
		if (!$currentUser) return;
		saving = true;
		await getSupabase()
			.from('users')
			.update({ display_name: displayNameInput.trim() })
			.eq('id', $currentUser.id);
		saving = false;
		saved = true;
		setTimeout(() => (saved = false), 2000);
	}

	async function selectEmojiAvatar(emoji: string) {
		avatarEmoji = emoji;
		avatarType = 'emoji';
		await getSupabase()
			.from('users')
			.update({ avatar_type: 'emoji', avatar_emoji: emoji })
			.eq('id', $currentUser!.id);
	}

	async function selectInitialsAvatar() {
		avatarType = 'initials';
		await getSupabase()
			.from('users')
			.update({ avatar_type: 'initials', avatar_emoji: null })
			.eq('id', $currentUser!.id);
	}

	function onFileInput(e: Event) {
		const input = e.currentTarget as HTMLInputElement;
		const file = input.files?.[0];
		if (file) cropperFile = file;
		input.value = '';
	}

	async function onCrop(blob: Blob) {
		cropperFile = null;
		if (!$currentUser) return;
		uploading = true;

		const path = `${$currentUser.id}/avatar.webp`;
		const { error: uploadError } = await getSupabase()
			.storage
			.from('avatars')
			.upload(path, blob, { upsert: true, contentType: 'image/webp' });

		if (uploadError) {
			uploading = false;
			return;
		}

		const { data: urlData } = await getSupabase()
			.storage
			.from('avatars')
			.createSignedUrl(path, 60 * 60 * 24 * 7); // 7 days

		const signedUrl = urlData?.signedUrl ?? null;

		await getSupabase()
			.from('users')
			.update({ avatar_type: 'upload', avatar_url: signedUrl })
			.eq('id', $currentUser.id);

		avatarType = 'upload';
		avatarUrl = signedUrl;
		uploading = false;
	}

	// Fase 9.1: persist the chosen theme to public.users.theme so it
	// follows the user across devices. ThemeToggle has already updated
	// localStorage + <html data-theme> synchronously — this is purely
	// the "save my preference server-side" step. Best-effort: a failed
	// UPDATE does not roll the UI choice back.
	async function persistTheme(next: ThemePreference) {
		if (!$currentUser) return;
		await getSupabase()
			.from('users')
			.update({ theme: next })
			.eq('id', $currentUser.id);
	}

	async function switchLanguage(lang: 'en' | 'es') {
		language = lang;
		setLanguageTag(lang);
		await getSupabase()
			.from('users')
			.update({ language: lang })
			.eq('id', $currentUser!.id);
	}

	// ── Fase 10.1: leave collective (CU-H06) ────────────────────────────────
	let leaveCandidate = $state<{ id: string; name: string } | null>(null);
	let leaveError = $state<string | null>(null);

	function startLeave(c: { id: string; name: string }) {
		leaveCandidate = c;
		leaveError = null;
	}

	function cancelLeave() {
		leaveCandidate = null;
		leaveError = null;
	}

	async function confirmLeave() {
		if (!leaveCandidate) return;
		const target = leaveCandidate;
		const supabase = getSupabase();
		const { error } = await supabase.rpc('leave_collective', { p_collective_id: target.id });
		if (error) {
			// P0001 = sole member → instruct to dissolve instead.
			leaveError = (error as { code?: string }).code === 'P0001'
				? m.settings_collectives_dissolve_hint()
				: error.message;
			return;
		}

		// Update local stores: drop the collective from userCollectives. If it
		// was the active one, switch to the next or kick to /onboarding.
		const remaining = $userCollectives.filter((c) => c.id !== target.id);
		userCollectives.set(remaining);

		if ($currentCollective?.id === target.id) {
			if (remaining.length > 0) {
				const next = remaining[0];
				currentCollective.set(next);
				localStorage.setItem('activeCollectiveId', next.id);
			} else {
				currentCollective.set(null);
				localStorage.removeItem('activeCollectiveId');
				leaveCandidate = null;
				await goto('/onboarding');
				return;
			}
		}

		leaveCandidate = null;
	}

	// ── Fase 10.5: delete account ───────────────────────────────────────────
	let showDeleteAccount = $state(false);
	let deleteConfirmInput = $state('');
	let deleteAccountError = $state<string | null>(null);
	let deleteInFlight = $state(false);

	const deleteConfirmWord = m.settings_delete_account_confirm_word();
	const deleteConfirmReady = $derived(deleteConfirmInput === deleteConfirmWord);

	function openDeleteAccount() {
		showDeleteAccount = true;
		deleteConfirmInput = '';
		deleteAccountError = null;
	}

	function closeDeleteAccount() {
		showDeleteAccount = false;
		deleteConfirmInput = '';
		deleteAccountError = null;
	}

	async function confirmDeleteAccount() {
		if (!deleteConfirmReady || deleteInFlight) return;
		deleteInFlight = true;
		deleteAccountError = null;
		const { error } = await getSupabase().rpc('delete_account');
		if (error) {
			deleteInFlight = false;
			deleteAccountError = (error as { code?: string }).code === 'P0003'
				? m.settings_delete_account_blocked()
				: error.message;
			return;
		}
		// GoTrue row gone; clear local + start logout (RP-initiated) so Keycloak
		// session is also ended. /logged-out is the post-logout landing.
		await logout();
	}
</script>

<div class="flex flex-1 flex-col overflow-hidden">
	<header class="px-8 pb-4 pt-8">
		<p class="mb-1 text-[13px] font-semibold uppercase tracking-[0.05em] text-text-secondary">
			{m.nav_settings()}
		</p>
		<h1 class="text-[20px] font-semibold tracking-[-0.01em] text-slate-900 dark:text-slate-50">
			{m.settings_title()}
		</h1>
	</header>

	<div class="flex-1 overflow-y-auto px-8 py-6">
		<div class="mx-auto max-w-md space-y-8">

			<!-- Avatar preview -->
			<div class="flex justify-center">
				<Avatar
					name={displayNameInput || $currentUser?.email || ''}
					type={avatarType}
					emoji={avatarEmoji}
					src={avatarUrl}
					size={80}
				/>
			</div>

			<!-- Display name -->
			<section>
				<label for="display-name" class="mb-1 block text-[13px] font-semibold uppercase tracking-[0.05em] text-text-secondary">
					{m.settings_display_name()}
				</label>
				<div class="relative">
					<input id="display-name"
						type="text"
						bind:value={displayNameInput}
						oninput={scheduleNameSave}
						maxlength="60"
						class="w-full rounded-lg border border-slate-300 bg-surface px-3 py-2 text-sm
							focus:border-slate-500 focus:outline-none dark:border-slate-600 dark:bg-slate-800"
					/>
					{#if saving}
						<span class="absolute right-3 top-2 text-xs text-text-secondary">{m.settings_saving()}</span>
					{:else if saved}
						<span class="absolute right-3 top-2 text-xs text-emerald-600">✓</span>
					{/if}
				</div>
			</section>

			<!-- Avatar -->
			<section>
				<p class="mb-3 text-[13px] font-semibold uppercase tracking-[0.05em] text-text-secondary">
					{m.settings_avatar()}
				</p>

				<!-- Mode tabs -->
				<div class="mb-4 flex gap-2">
					{#each [['initials', m.settings_avatar_initials()], ['emoji', m.settings_avatar_emoji()], ['upload', m.settings_avatar_upload()]] as [mode, label]}
						<button
							onclick={() => mode === 'initials' ? selectInitialsAvatar() : avatarType = mode as typeof avatarType}
							class="rounded-md px-3 py-1.5 text-xs font-medium transition-colors
								{avatarType === mode
								? 'bg-slate-900 text-white dark:bg-slate-50 dark:text-slate-900'
								: 'border border-slate-300 text-slate-600 hover:bg-slate-50 dark:border-slate-600 dark:text-slate-400'}"
						>
							{label}
						</button>
					{/each}
				</div>

				{#if avatarType === 'emoji'}
					<div class="grid grid-cols-8 gap-1">
						{#each EMOJI_GRID as e}
							<button
								onclick={() => selectEmojiAvatar(e)}
								class="flex h-9 w-9 items-center justify-center rounded-md text-xl
									{avatarEmoji === e ? 'bg-slate-900 dark:bg-slate-100' : 'hover:bg-slate-100 dark:hover:bg-slate-700'}"
								aria-label={e}
							>{e}</button>
						{/each}
					</div>
				{:else if avatarType === 'upload'}
					<label
						class="flex cursor-pointer items-center gap-2 rounded-lg border border-dashed
							border-slate-300 px-4 py-3 text-sm text-slate-600 hover:border-slate-500
							dark:border-slate-600 dark:text-slate-400"
					>
						<span>{uploading ? m.loading() : m.settings_avatar_upload_btn()}</span>
						<input type="file" accept="image/*" class="hidden" onchange={onFileInput} />
					</label>
				{/if}
			</section>

			<!-- Appearance (Fase 9.1) -->
			<section>
				<p class="mb-3 text-[13px] font-semibold uppercase tracking-[0.05em] text-text-secondary">
					{m.settings_appearance()}
				</p>
				<ThemeToggle onChange={persistTheme} />
			</section>

			<!-- Language -->
			<section>
				<p class="mb-3 text-[13px] font-semibold uppercase tracking-[0.05em] text-text-secondary">
					{m.settings_language()}
				</p>
				<div class="flex gap-2">
					{#each [['en', 'English'], ['es', 'Español']] as [code, label]}
						<button
							onclick={() => switchLanguage(code as 'en' | 'es')}
							class="rounded-md px-4 py-2 text-sm font-medium transition-colors
								{language === code
								? 'bg-slate-900 text-white dark:bg-slate-50 dark:text-slate-900'
								: 'border border-slate-300 text-slate-600 hover:bg-slate-50 dark:border-slate-600 dark:text-slate-400'}"
						>
							{label}
						</button>
					{/each}
				</div>
			</section>

			<!-- Fase 9.3.3 — sync conflicts debug panel. Gated on
			     import.meta.env.DEV inside the component itself. -->
			<SyncConflictsPanel />

			<!-- Collectives (Fase 10.1) -->
			<section>
				<p class="mb-3 text-[13px] font-semibold uppercase tracking-[0.05em] text-text-secondary">
					{m.settings_collectives()}
				</p>
				{#if $userCollectives.length === 0}
					<p class="text-sm text-text-secondary">{m.settings_collectives_empty()}</p>
				{:else}
					<ul>
						{#each $userCollectives as c (c.id)}
							<li data-testid="collective-row-{c.id}" class="flex items-center gap-3 py-2">
								<span class="text-lg">{c.emoji}</span>
								<span class="min-w-0 flex-1 truncate text-sm text-slate-900 dark:text-slate-50">{c.name}</span>
								<button
									type="button"
									data-testid="leave-collective-{c.id}"
									onclick={() => startLeave(c)}
									class="rounded-md border border-slate-300 px-3 py-1 text-xs font-medium text-slate-700
										hover:bg-slate-50 dark:border-slate-600 dark:text-slate-300 dark:hover:bg-slate-800"
								>
									{m.settings_collectives_leave()}
								</button>
							</li>
						{/each}
					</ul>
				{/if}
			</section>

			<!-- Tags (Fase 11.3.6) — collective-scoped tag management. Lives in
			     settings because the tag flow on the list view is per-item; the
			     full list of tags + rename + recolor + delete is admin-style. -->
			<section data-testid="settings-tags-section" class="pt-8">
				<p class="mb-3 text-[13px] font-semibold uppercase tracking-[0.05em] text-text-secondary">
					{m.settings_tags_section()}
				</p>
				{#if $tagsStore.length === 0}
					<p class="text-sm text-text-secondary">{m.settings_tags_empty()}</p>
				{:else}
					<ul class="space-y-2">
						{#each $tagsStore as tag (tag.id)}
							<li
								data-testid="settings-tag-row"
								data-tag={tag.name}
								class="flex items-center gap-3 rounded-md bg-surface-raised px-3 py-2"
							>
								{#if editingTagId === tag.id}
									<input
										bind:value={editingTagName}
										onblur={commitEditTag}
										onkeydown={(e) => {
											if (e.key === 'Enter') {
												e.preventDefault();
												void commitEditTag();
											} else if (e.key === 'Escape') cancelEditTag();
										}}
										class="min-w-0 flex-1 rounded bg-background px-2 py-1 text-sm text-text-primary outline-none ring-1 ring-black/10 focus:ring-slate-900 dark:ring-white/10 dark:focus:ring-slate-100"
									/>
								{:else}
									<button
										type="button"
										onclick={() => startEditTag(tag)}
										class="min-w-0 flex-1 text-left"
									>
										<TagChip name={tag.name} color={tag.color} />
									</button>
								{/if}
								<div class="flex shrink-0 items-center gap-1" aria-label={m.settings_tags_color()}>
									{#each TAG_COLORS as color}
										<button
											type="button"
											onclick={() => handleRecolor(tag, color)}
											aria-label={color}
											class="h-4 w-4 rounded-full bg-{color}-500 {tag.color === color ? 'ring-2 ring-offset-1 ring-slate-700 dark:ring-slate-300' : 'opacity-70 hover:opacity-100'}"
										></button>
									{/each}
								</div>
								<button
									type="button"
									data-testid="settings-tag-delete"
									onclick={() => handleDeleteTag(tag.id)}
									class="rounded-md border border-slate-300 px-2 py-1 text-xs font-medium text-slate-700 hover:bg-slate-50 dark:border-slate-600 dark:text-slate-300 dark:hover:bg-slate-800"
								>
									{m.settings_tags_delete()}
								</button>
							</li>
						{/each}
					</ul>
				{/if}
			</section>

			<!-- Account — spacing creates the separation, not a border line -->
			<section class="pt-8">
				<p class="mb-3 text-[13px] font-semibold uppercase tracking-[0.05em] text-text-secondary">
					{m.settings_account()}
				</p>
				<a
					href={keycloakAccountUrl}
					target="_blank"
					rel="noopener noreferrer"
					class="mb-3 flex items-center gap-2 text-sm text-slate-600 underline dark:text-slate-400"
				>
					{m.settings_keycloak_link()} ↗
				</a>
				<button
					onclick={logout}
					class="rounded-md bg-destructive px-4 py-2 text-sm font-semibold text-white hover:opacity-90"
				>
					{m.settings_logout()}
				</button>
			</section>

			<!-- Danger zone (Fase 10.5) -->
			<section class="pt-8">
				<p class="mb-3 text-[13px] font-semibold uppercase tracking-[0.05em] text-destructive">
					{m.settings_danger_zone()}
				</p>
				<p class="mb-3 text-sm text-text-secondary">{m.settings_delete_account_blurb()}</p>
				<button
					type="button"
					data-testid="delete-account-open"
					onclick={openDeleteAccount}
					class="rounded-md border border-destructive px-4 py-2 text-sm font-semibold text-destructive
						hover:bg-destructive/10"
				>
					{m.settings_delete_account()}
				</button>
			</section>
		</div>
	</div>
</div>

{#if cropperFile}
	<ImageCropper
		file={cropperFile}
		{onCrop}
		onCancel={() => (cropperFile = null)}
	/>
{/if}

<!-- Leave-collective confirmation modal (Fase 10.1) -->
{#if leaveCandidate}
	<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4" data-testid="leave-modal">
		<div class="w-full max-w-sm rounded-lg bg-surface p-5 shadow-[0px_20px_40px_rgba(15,23,42,0.2)] dark:bg-surface-raised">
			<h2 class="mb-2 text-base font-semibold text-slate-900 dark:text-slate-50">
				{m.settings_collectives_confirm_leave_title({ name: leaveCandidate.name })}
			</h2>
			<p class="mb-4 text-sm text-text-secondary">{m.settings_collectives_confirm_leave_body()}</p>
			{#if leaveError}
				<p class="mb-3 text-sm text-destructive" data-testid="leave-error">{leaveError}</p>
			{/if}
			<div class="flex justify-end gap-2">
				<button
					type="button"
					onclick={cancelLeave}
					class="rounded-md px-3 py-1.5 text-sm font-medium text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800"
				>
					{m.common_cancel()}
				</button>
				<button
					type="button"
					data-testid="confirm-leave-collective"
					onclick={confirmLeave}
					class="rounded-md bg-destructive px-4 py-1.5 text-sm font-semibold text-white hover:opacity-90"
				>
					{m.settings_collectives_confirm_leave_button()}
				</button>
			</div>
		</div>
	</div>
{/if}

<!-- Delete-account modal (Fase 10.5) -->
{#if showDeleteAccount}
	<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4" data-testid="delete-account-modal">
		<div class="w-full max-w-md rounded-lg bg-surface p-5 shadow-[0px_20px_40px_rgba(15,23,42,0.2)] dark:bg-surface-raised">
			<h2 class="mb-2 text-base font-semibold text-slate-900 dark:text-slate-50">
				{m.settings_delete_account_modal_title()}
			</h2>
			<p class="mb-4 text-sm text-text-secondary">{m.settings_delete_account_modal_body()}</p>
			<label for="delete-account-input" class="mb-1 block text-xs font-medium text-text-secondary">
				{m.settings_delete_account_confirm_label({ word: deleteConfirmWord })}
			</label>
			<input
				id="delete-account-input"
				data-testid="delete-account-confirm-input"
				type="text"
				bind:value={deleteConfirmInput}
				class="mb-3 w-full rounded-lg border border-slate-300 bg-surface px-3 py-2 text-sm
					focus:border-slate-500 focus:outline-none dark:border-slate-600 dark:bg-slate-800"
			/>
			{#if deleteAccountError}
				<p class="mb-3 text-sm text-destructive" data-testid="delete-account-error">{deleteAccountError}</p>
			{/if}
			<div class="flex justify-end gap-2">
				<button
					type="button"
					onclick={closeDeleteAccount}
					class="rounded-md px-3 py-1.5 text-sm font-medium text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800"
				>
					{m.common_cancel()}
				</button>
				<button
					type="button"
					data-testid="confirm-delete-account"
					disabled={!deleteConfirmReady || deleteInFlight}
					onclick={confirmDeleteAccount}
					class="rounded-md bg-destructive px-4 py-1.5 text-sm font-semibold text-white hover:opacity-90 disabled:opacity-50"
				>
					{m.settings_delete_account_button()}
				</button>
			</div>
		</div>
	</div>
{/if}
