<script lang="ts">
	import Building2 from '@lucide/svelte/icons/building-2';
	import ChevronDown from '@lucide/svelte/icons/chevron-down';
	import Globe from '@lucide/svelte/icons/globe';
	import KeyRound from '@lucide/svelte/icons/key-round';
	import { SvelteSet } from 'svelte/reactivity';
	import { api } from '$lib/api/client';
	import type { components } from '$lib/api/schema';
	import Button from '$lib/components/Button.svelte';
	import Popover from '$lib/components/Popover.svelte';
	import Select from '$lib/components/Select.svelte';
	import { visibilityLabel } from '$lib/documents/presentation';
	import { m } from '$lib/paraglide/messages';

	// "Who can see this?" — the answer is on the chip, the controls are one
	// click behind it. Visibility and the extra departments are the same
	// question asked twice, so they are answered in one place; both are the
	// owner's call, which is why a reviewer sees the answer and no controls.
	type Department = components['schemas']['DepartmentOut'];
	type DocumentDetail = components['schemas']['DocumentDetail'];

	let {
		document: doc,
		canManage,
		onChanged
	}: {
		document: DocumentDetail;
		canManage: boolean;
		onChanged?: () => Promise<void> | void;
	} = $props();

	let open = $state(false);
	let departments = $state<Department[]>([]);
	const selected = new SvelteSet<string>();
	let busy = $state(false);
	let error = $state<string | null>(null);
	// A change that would remove the editing admin's own access is held until
	// they confirm it; `pending` is what they are being asked about.
	let pending = $state<{ visibility?: DocumentDetail['visibility'] } | null>(null);

	const shared = $derived(doc.shared_departments);
	const shareable = $derived(departments.filter((entry) => entry.id !== doc.department_id));
	const VisibilityIcon = $derived(doc.visibility === 'restricted' ? KeyRound : Globe);

	$effect(() => {
		if (!open) return;
		selected.clear();
		for (const department of shared) selected.add(department.id);
		error = null;
		pending = null;
		void api.GET('/api/departments').then(({ data }) => (departments = data ?? []));
	});

	function toggle(id: string) {
		if (selected.has(id)) selected.delete(id);
		else selected.add(id);
	}

	async function setVisibility(visibility: DocumentDetail['visibility'], confirmLockout = false) {
		busy = true;
		error = null;
		const { response, error: err } = await api.PATCH('/api/documents/{document_id}', {
			params: { path: { document_id: doc.id } },
			body: { visibility, confirm_lockout: confirmLockout }
		});
		busy = false;
		if (response.ok) {
			pending = null;
			await onChanged?.();
			return;
		}
		if ((err as { code?: string })?.code === 'self_lockout_warning' && !confirmLockout) {
			pending = { visibility };
			return;
		}
		error = m.visibility_save_failed();
	}

	async function saveDepartments(confirmLockout = false) {
		busy = true;
		error = null;
		const { response, error: err } = await api.PUT('/api/documents/{document_id}/departments', {
			params: { path: { document_id: doc.id } },
			body: { department_ids: [...selected], confirm_lockout: confirmLockout }
		});
		busy = false;
		if (response.ok) {
			pending = null;
			await onChanged?.();
			return;
		}
		if ((err as { code?: string })?.code === 'self_lockout_warning' && !confirmLockout) {
			pending = {};
			return;
		}
		error = m.sharing_save_failed();
	}
</script>

<Popover bind:open contentClass="w-80" triggerLabel={m.access_popover_title()}>
	{#snippet trigger()}
		<span
			class="flex items-center gap-1.5 rounded-full border border-border px-2.5 py-1 text-xs text-ink-muted transition-colors hover:border-border-strong hover:text-ink"
			data-testid="access-chip"
		>
			<VisibilityIcon size={12} />
			{visibilityLabel(doc.visibility)}
			{#if shared.length > 0}
				<span class="text-ink-muted">{m.access_plus_departments({ count: shared.length })}</span>
			{/if}
			<ChevronDown size={12} class="opacity-60" />
		</span>
	{/snippet}

	<div class="flex flex-col gap-3" data-testid="access-controls">
		<p class="text-sm font-medium">{m.access_popover_title()}</p>

		{#if canManage}
			<label class="flex flex-col gap-1 text-xs text-ink-muted">
				{m.visibility_label()}
				<Select
					value={doc.visibility}
					disabled={busy}
					onchange={(event) =>
						setVisibility(event.currentTarget.value as DocumentDetail['visibility'])}
					data-testid="visibility-select"
				>
					<option value="public">{m.document_visibility_public()}</option>
					<option value="department">{m.document_visibility_department()}</option>
					<option value="restricted">{m.document_visibility_restricted()}</option>
				</Select>
			</label>
		{:else}
			<p class="text-sm">{m.document_visibility_line({ visibility: doc.visibility })}</p>
		{/if}

		<div class="flex flex-col gap-1">
			<p class="text-xs text-ink-muted">{m.access_extra_departments()}</p>
			{#if canManage}
				{#if shareable.length === 0}
					<p class="text-sm text-ink-muted">{m.sharing_none_shareable()}</p>
				{:else}
					<ul class="flex max-h-44 flex-col overflow-y-auto">
						{#each shareable as department (department.id)}
							<li>
								<label
									class="flex cursor-pointer items-center gap-2 rounded-md px-1.5 py-1 text-sm hover:bg-surface-sunken"
								>
									<input
										type="checkbox"
										class="accent-accent"
										checked={selected.has(department.id)}
										onchange={() => toggle(department.id)}
									/>
									{department.name}
								</label>
							</li>
						{/each}
					</ul>
					<Button
						size="sm"
						class="self-start"
						disabled={busy}
						onclick={() => saveDepartments()}
						data-testid="share-save"
					>
						{m.sharing_save()}
					</Button>
				{/if}
			{:else if shared.length === 0}
				<p class="text-sm text-ink-muted">{m.access_no_extra_departments()}</p>
			{:else}
				<ul class="flex flex-col gap-1 text-sm">
					{#each shared as department (department.id)}
						<li class="flex items-center gap-1.5">
							<Building2 size={13} class="text-ink-muted" />
							{department.name}
						</li>
					{/each}
				</ul>
			{/if}
		</div>

		{#if pending}
			<div class="flex flex-col gap-2 rounded-lg bg-warning-muted px-3 py-2">
				<span class="text-sm text-warning" role="alert">{m.sharing_lockout_warning()}</span>
				<div class="flex gap-2">
					<Button
						variant="danger"
						size="sm"
						disabled={busy}
						onclick={() =>
							pending?.visibility ? setVisibility(pending.visibility, true) : saveDepartments(true)}
					>
						{m.sharing_lockout_confirm()}
					</Button>
					<Button variant="ghost" size="sm" onclick={() => (pending = null)}>
						{m.common_cancel()}
					</Button>
				</div>
			</div>
		{/if}
		{#if error}
			<p role="alert" class="text-sm text-danger">{error}</p>
		{/if}
	</div>
</Popover>
