<script lang="ts">
	import { base } from '$app/paths';
	import { page } from '$app/state';
	import { api, ApiError, type FileMeta } from '$lib/api';
	import ConfirmModal from '$lib/ConfirmModal.svelte';

	let slug = $derived(page.params.slug ?? '');
	let collection = $state('');
	let activeCollection = $state('');
	let files = $state<FileMeta[]>([]);
	let nextCursor = $state<string | null>(null);
	let loading = $state(false);
	let error = $state<string | null>(null);
	let fileToRemove = $state<FileMeta | null>(null);
	let removing = $state(false);
	/// F-U-003: hints derived from registered `files` triggers. No
	/// backend endpoint lists known collections; this is the next-best
	/// approximation — operators see "we've heard about these patterns"
	/// instead of having to guess.
	let collectionHints = $state<string[]>([]);

	async function loadCollectionHints() {
		try {
			const r = await api.triggers.list(slug);
			const seen = new Set<string>();
			for (const t of r.triggers) {
				if (t.details.kind === 'files' && t.details.collection_glob) {
					seen.add(t.details.collection_glob);
				}
			}
			collectionHints = Array.from(seen).sort();
		} catch {
			collectionHints = [];
		}
	}

	$effect(() => {
		void slug;
		void loadCollectionHints();
	});

	async function loadFiles(cursor?: string) {
		const c = collection.trim();
		if (!c) {
			error = 'Enter a collection name to list its files.';
			return;
		}
		loading = true;
		error = null;
		try {
			const res = await api.files.list(slug, c, { cursor, limit: 100 });
			if (cursor) {
				files = [...files, ...res.files];
			} else {
				files = res.files;
				activeCollection = c;
			}
			nextCursor = res.next_cursor;
		} catch (e) {
			error = e instanceof ApiError ? e.message : String(e);
		} finally {
			loading = false;
		}
	}

	async function confirmRemove() {
		if (!fileToRemove) return;
		removing = true;
		try {
			await api.files.remove(slug, fileToRemove.collection, fileToRemove.id);
			files = files.filter((f) => f.id !== fileToRemove!.id);
			fileToRemove = null;
		} catch (e) {
			error = e instanceof ApiError ? e.message : String(e);
		} finally {
			removing = false;
		}
	}

	function fmtTime(iso: string): string {
		return new Date(iso).toLocaleString();
	}

	// F-U-009: best-effort clipboard copy; silently no-ops where the
	// browser doesn't expose navigator.clipboard (e.g. plain-http LAN).
	function copyToClipboard(text: string) {
		void navigator.clipboard?.writeText(text);
	}

	function fmtSize(bytes: number): string {
		if (bytes < 1024) return `${bytes} B`;
		if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
		return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
	}
</script>

<svelte:head>
	<title>Files · {slug} · PiCloud</title>
</svelte:head>

<div class="panel">
	<header class="panel-head">
		<div>
			<h2>Files</h2>
			<p class="subtitle">
				Browse and delete stored blobs by collection. Uploads happen from scripts via
				<code>files::collection(c).create(…)</code>.
			</p>
		</div>
	</header>

	<form
		class="collection-form"
		onsubmit={(e) => {
			e.preventDefault();
			void loadFiles();
		}}
	>
		<label>
			<span>Collection</span>
			<input
				bind:value={collection}
				placeholder="avatars"
				list="files-collection-hints"
				required
			/>
		</label>
		<datalist id="files-collection-hints">
			{#each collectionHints as h}
				<option value={h}></option>
			{/each}
		</datalist>
		<button type="submit" disabled={loading || !collection.trim()}>
			{loading ? 'Loading…' : 'List files'}
		</button>
	</form>
	{#if collectionHints.length > 0}
		<p class="hint">
			Known collection patterns from registered files triggers:
			{#each collectionHints as h, i}
				{#if i > 0}, {/if}
				<button class="hint-chip" type="button" onclick={() => (collection = h)}>{h}</button>
			{/each}
		</p>
	{:else}
		<p class="hint">
			No files triggers registered yet. Once you register a files trigger from the Triggers tab,
			its collection glob will appear here as a suggestion.
		</p>
	{/if}

	{#if error}
		<div class="error">{error}</div>
	{/if}

	{#if activeCollection}
		{#if files.length === 0 && !loading}
			<p class="muted">No files in collection <code>{activeCollection}</code>.</p>
		{:else}
			<table>
				<thead>
					<tr>
						<th>Name</th>
						<th>Content type</th>
						<th>Size</th>
						<th>Created</th>
						<th>ID</th>
						<th></th>
					</tr>
				</thead>
				<tbody>
					{#each files as f (f.id)}
						<tr>
							<td>{f.name}</td>
							<td><code>{f.content_type}</code></td>
							<td>{fmtSize(f.size)}</td>
							<td>{fmtTime(f.created_at)}</td>
							<td class="mono small">
								{f.id}
								<button
									type="button"
									class="copy-btn"
									title="Copy ID"
									onclick={() => copyToClipboard(f.id)}
								>
									⧉
								</button>
							</td>
							<td>
								<a
									class="secondary"
									href={api.files.downloadUrl(slug, activeCollection, f.id)}
									download={f.name}
								>
									Download
								</a>
								<button type="button" class="danger" onclick={() => (fileToRemove = f)}>
									Delete
								</button>
							</td>
						</tr>
					{/each}
				</tbody>
			</table>
			{#if nextCursor}
				<button type="button" class="secondary" onclick={() => loadFiles(nextCursor ?? undefined)}>
					Load more
				</button>
			{/if}
		{/if}
	{/if}
</div>

{#if fileToRemove}
	<ConfirmModal
		title="Delete file"
		variant="danger"
		confirmLabel="Delete file"
		onConfirm={confirmRemove}
		onCancel={() => (fileToRemove = null)}
	>
		<p>
			Delete <strong>{fileToRemove.name}</strong> ({fmtSize(fileToRemove.size)}) from collection
			<code>{fileToRemove.collection}</code>? This removes both the metadata row and the bytes on
			disk and cannot be undone.
		</p>
		{#if removing}<p class="muted">Deleting…</p>{/if}
	</ConfirmModal>
{/if}

<style>
	.panel-head {
		margin-bottom: 1rem;
	}
	.panel-head h2 {
		margin: 0.25rem 0;
		font-size: 1.125rem;
	}
	.subtitle {
		color: var(--text-muted);
		font-size: 0.9rem;
	}
	.collection-form {
		display: flex;
		align-items: flex-end;
		gap: 0.75rem;
		margin-bottom: 1rem;
	}
	.collection-form label {
		display: flex;
		flex-direction: column;
		gap: 0.25rem;
		font-size: 0.85rem;
	}
	table {
		width: 100%;
		border-collapse: collapse;
		font-size: 0.9rem;
	}
	th,
	td {
		text-align: left;
		padding: 0.4rem 0.6rem;
		border-bottom: 1px solid var(--border);
	}
	.mono {
		font-family: monospace;
	}
	.small {
		font-size: 0.78rem;
	}
	.muted {
		color: var(--text-muted);
	}
	.hint {
		color: var(--text-muted);
		font-size: 0.85rem;
		margin: 0.5rem 0 1rem;
	}
	.hint-chip {
		background: var(--bg-elevated);
		color: var(--text-primary);
		border: 1px solid var(--border);
		border-radius: 0.25rem;
		padding: 0.1rem 0.4rem;
		font-size: 0.8rem;
		cursor: pointer;
		font-family: monospace;
	}
	.hint-chip:hover {
		border-color: var(--color-link);
	}
	.error {
		color: var(--color-danger);
		margin: 0.5rem 0;
	}
	button.danger {
		background: var(--color-danger-bg);
		color: var(--color-danger-fg);
		border: 1px solid var(--color-danger-border);
	}
</style>
