<script>
	import Icon from '$lib/Icon.svelte';

	export let users = [];
	export let selectedUserId = null;

	let sourceUserId = null;
	let mediaType = 'Movie';
	let searchTerm = '';
	let searchResults = [];
	let inspections = {};
	let searchBusy = false;
	let inspectAllBusy = false;
	let applyAllBusy = false;
	let searchError = '';
	let actionError = '';
	let actionMessage = '';

	$: sourceUsers = users.filter((user) => user.embyGuid);
	$: if (!sourceUsers.some((user) => user.id === sourceUserId)) {
		sourceUserId = sourceUsers.some((user) => user.id === selectedUserId)
			? selectedUserId
			: (sourceUsers[0]?.id || null);
	}
	$: sourceUser = users.find((user) => user.id === sourceUserId);
	$: inspectedCount = Object.values(inspections).filter((entry) => entry?.item).length;
	$: readyToApplyCount = Object.values(inspections).filter(
		(entry) => entry?.selectedGenre && !entry?.error
	).length;

	let lastSearchContext = '';
	$: {
		const nextSearchContext = `${sourceUserId || ''}:${mediaType}`;
		if (nextSearchContext !== lastSearchContext) {
			lastSearchContext = nextSearchContext;
			searchResults = [];
			inspections = {};
			searchError = '';
			actionError = '';
			actionMessage = '';
		}
	}

	async function searchLibrary() {
		actionError = '';
		actionMessage = '';
		searchError = '';

		if (!sourceUser?.embyGuid) {
			searchError = 'Select a linked Emby user first.';
			return;
		}

		if (!searchTerm.trim()) {
			searchResults = [];
			inspections = {};
			return;
		}

		searchBusy = true;
		try {
			const params = new URLSearchParams({
				userId: sourceUser.embyGuid,
				term: searchTerm.trim(),
				types: mediaType,
				limit: '25'
			});
			const response = await fetch(`/api/emby-item-search?${params.toString()}`);
			const body = await response.json().catch(() => ({ items: [] }));
			if (!response.ok) throw new Error(body.message || body.error || response.statusText);
			searchResults = body.items || [];
			inspections = {};
			actionMessage = searchResults.length
				? `Found ${searchResults.length} ${mediaType === 'Movie' ? 'movie' : 'show'} matches.`
				: 'No matching items were found.';
		} catch (err) {
			searchError = err.message;
			searchResults = [];
			inspections = {};
		} finally {
			searchBusy = false;
		}
	}

	async function inspectItem(item) {
		if (!sourceUser?.embyGuid || !item?.id) return;

		inspections = {
			...inspections,
			[item.id]: {
				...(inspections[item.id] || {}),
				loading: true,
				error: '',
				updated: false
			}
		};

		try {
			const params = new URLSearchParams({
				userId: sourceUser.embyGuid,
				itemId: item.id
			});
			const response = await fetch(`/api/emby-genre-cleanup?${params.toString()}`);
			const body = await response.json().catch(() => ({}));
			if (!response.ok) throw new Error(body.message || body.error || response.statusText);

			inspections = {
				...inspections,
				[item.id]: {
					loading: false,
					error: '',
					updated: inspections[item.id]?.updated || false,
					...body,
					selectedGenre: body.suggestedGenre || body.tmdb?.genres?.[0] || ''
				}
			};
		} catch (err) {
			inspections = {
				...inspections,
				[item.id]: {
					...(inspections[item.id] || {}),
					loading: false,
					error: err.message
				}
			};
		}
	}

	function updateSelectedGenre(itemId, genreName) {
		inspections = {
			...inspections,
			[itemId]: {
				...(inspections[itemId] || {}),
				selectedGenre: genreName
			}
		};
	}

	function syncSearchResultGenres(itemId, genreName) {
		searchResults = searchResults.map((item) =>
			item.id === itemId
				? {
					...item,
					genres: genreName ? [genreName] : []
				}
				: item
		);
	}

	async function applyGenre(item) {
		const inspection = inspections[item.id];
		if (!sourceUser?.embyGuid || !inspection?.selectedGenre) return;

		actionError = '';
		actionMessage = '';
		inspections = {
			...inspections,
			[item.id]: {
				...inspection,
				applying: true,
				error: ''
			}
		};

		try {
			const response = await fetch('/api/emby-genre-cleanup', {
				method: 'POST',
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify({
					userId: sourceUser.embyGuid,
					itemId: item.id,
					genreName: inspection.selectedGenre
				})
			});
			const body = await response.json().catch(() => ({}));
			if (!response.ok) throw new Error(body.message || body.error || response.statusText);

			inspections = {
				...inspections,
				[item.id]: {
					loading: false,
					applying: false,
					error: '',
					updated: true,
					...body,
					selectedGenre: inspection.selectedGenre
				}
			};
			syncSearchResultGenres(item.id, inspection.selectedGenre);
			actionMessage = `Updated "${item.name}" to ${inspection.selectedGenre}.`;
		} catch (err) {
			inspections = {
				...inspections,
				[item.id]: {
					...inspection,
					applying: false,
					error: err.message
				}
			};
			actionError = err.message;
		}
	}

	async function inspectAllResults() {
		if (!searchResults.length) return;
		inspectAllBusy = true;
		actionError = '';
		actionMessage = '';

		try {
			for (const item of searchResults) {
				if (!inspections[item.id]?.item) {
					await inspectItem(item);
				}
			}
			actionMessage = `Inspected ${searchResults.length} item${searchResults.length === 1 ? '' : 's'}.`;
		} finally {
			inspectAllBusy = false;
		}
	}

	async function applyToAllInspected() {
		const pendingItems = searchResults.filter((item) => {
			const inspection = inspections[item.id];
			return inspection?.selectedGenre && !inspection?.error;
		});
		if (!pendingItems.length) return;

		applyAllBusy = true;
		actionError = '';
		actionMessage = '';

		try {
			for (const item of pendingItems) {
				await applyGenre(item);
			}
			actionMessage = `Applied single-genre cleanup to ${pendingItems.length} item${pendingItems.length === 1 ? '' : 's'}.`;
		} finally {
			applyAllBusy = false;
		}
	}
</script>

<div class="genre-page">
	<div class="page-header">
		<div>
			<div class="page-title-row">
				<span class="page-icon"><Icon name="search" size={18} /></span>
				<h2>Genre Cleanup</h2>
			</div>
			<p class="page-copy">Search movies or shows, compare Emby genres with TMDB genres, and reduce each title to one genre.</p>
		</div>
		<div class="page-actions">
			<button class="btn ghost" on:click={inspectAllResults} disabled={!searchResults.length || inspectAllBusy || searchBusy || applyAllBusy}>
				{inspectAllBusy ? 'Inspecting…' : 'Inspect all'}
			</button>
			<button class="btn accent" on:click={applyToAllInspected} disabled={!readyToApplyCount || inspectAllBusy || searchBusy || applyAllBusy}>
				{applyAllBusy ? 'Applying…' : `Apply all (${readyToApplyCount})`}
			</button>
		</div>
	</div>

	<div class="genre-grid">
		<div class="builder-main">
			<section class="builder-card hero-card">
				<div class="field-grid">
					<label class="field">
						<span class="field-label">Source user</span>
						<select bind:value={sourceUserId}>
							{#each sourceUsers as user}
								<option value={user.id}>{user.name}</option>
							{/each}
						</select>
					</label>
					<label class="field">
						<span class="field-label">Media type</span>
						<select bind:value={mediaType}>
							<option value="Movie">Movies</option>
							<option value="Series">TV Shows</option>
						</select>
					</label>
				</div>
				<div class="lookup-row">
					<input
						type="text"
						bind:value={searchTerm}
						on:keydown={(event) => event.key === 'Enter' && searchLibrary()}
						placeholder={mediaType === 'Movie' ? 'Search movies...' : 'Search shows...'}
					/>
					<button class="btn ghost" on:click={searchLibrary} disabled={searchBusy}>
						{searchBusy ? 'Searching…' : 'Search'}
					</button>
				</div>
				<p class="profile-note">The suggested genre uses TMDB order as a hint, but you can change the choice before writing it back to Emby.</p>
			</section>

			{#if searchError}
				<div class="status error">{searchError}</div>
			{/if}
			{#if actionError}
				<div class="status error">{actionError}</div>
			{:else if actionMessage}
				<div class="status ok">{actionMessage}</div>
			{/if}

			<section class="builder-card">
				<div class="card-header">
					<div class="card-title-row">
						<Icon name="items" size={16} />
						<h3>Matches</h3>
					</div>
					<span class="card-copy">Inspect one title at a time or batch the current search results.</span>
				</div>

				{#if !searchBusy && !searchResults.length}
					<div class="empty-note">Search for a movie or show to start cleaning up its genres.</div>
				{:else}
					<div class="result-list">
						{#each searchResults as item}
							{@const inspection = inspections[item.id]}
							<div class="result-card">
								<div class="result-header">
									<div>
										<div class="result-name">{item.name}</div>
										<div class="result-meta">
											{item.type}{item.year ? ` · ${item.year}` : ''}
											{#if inspection?.updated} · updated{/if}
										</div>
									</div>
									<button class="btn ghost small" on:click={() => inspectItem(item)} disabled={inspection?.loading || inspection?.applying || applyAllBusy}>
										{inspection?.loading ? 'Inspecting…' : 'Inspect'}
									</button>
								</div>

								<div class="genre-row">
									<span class="genre-label">Emby</span>
									<span class="genre-value">{(inspection?.currentGenres || item.genres || []).join(', ') || 'None'}</span>
								</div>

								{#if inspection?.error}
									<div class="status error inline-status">{inspection.error}</div>
								{:else if inspection?.item}
									<div class="genre-panel">
										<div class="genre-row">
											<span class="genre-label">TMDB</span>
											<span class="genre-value">{inspection.tmdb?.genres?.join(', ') || 'No genres returned'}</span>
										</div>
										<div class="genre-row">
											<span class="genre-label">Match</span>
											<span class="genre-value">
												{#if inspection.tmdb?.tmdbId}
													{inspection.tmdb.source === 'providerId' ? 'Provider ID match' : 'Title search match'} · TMDB {inspection.tmdb.tmdbId}
												{:else}
													No TMDB match
												{/if}
											</span>
										</div>

										{#if inspection.tmdb?.genres?.length}
											<div class="apply-grid">
												<label class="field">
													<span class="field-label">Single genre</span>
													<select
														value={inspection.selectedGenre}
														on:change={(event) => updateSelectedGenre(item.id, event.target.value)}
													>
														{#each inspection.tmdb.genres as genre}
															<option value={genre}>{genre}</option>
														{/each}
													</select>
												</label>
												<div class="apply-actions">
													<button class="btn accent" on:click={() => applyGenre(item)} disabled={!inspection.selectedGenre || inspection.applying || applyAllBusy}>
														{inspection.applying ? 'Applying…' : 'Apply single genre'}
													</button>
												</div>
											</div>
										{/if}
									</div>
								{/if}
							</div>
						{/each}
					</div>
				{/if}
			</section>
		</div>

		<aside class="builder-side">
			<section class="builder-card sticky-card">
				<div class="card-header">
					<div class="card-title-row">
						<Icon name="spark" size={16} />
						<h3>Summary</h3>
					</div>
				</div>
				<div class="detail-row">
					<span>Search results</span>
					<strong>{searchResults.length}</strong>
				</div>
				<div class="detail-row">
					<span>Inspected</span>
					<strong>{inspectedCount}</strong>
				</div>
				<div class="detail-row">
					<span>Ready to apply</span>
					<strong>{readyToApplyCount}</strong>
				</div>
				<p class="detail-message">
					TMDB can return multiple genres. This tool lets you use the first returned genre as a starting point without forcing that choice.
				</p>
			</section>
		</aside>
	</div>
</div>

<style>
	.genre-page {
		display: flex;
		flex-direction: column;
		gap: 18px;
	}
	.page-header {
		display: flex;
		justify-content: space-between;
		gap: 16px;
		align-items: flex-start;
	}
	.page-title-row,
	.card-title-row {
		display: flex;
		align-items: center;
		gap: 10px;
	}
	h2,
	h3 {
		margin: 0;
		color: var(--text);
	}
	h2 {
		font-size: 24px;
		font-weight: 800;
	}
	h3 {
		font-size: 15px;
		font-weight: 800;
	}
	.page-copy,
	.card-copy {
		margin: 6px 0 0;
		font-size: 13px;
		line-height: 1.5;
		color: var(--text-muted);
	}
	.page-actions {
		display: flex;
		gap: 12px;
		flex-wrap: wrap;
	}
	.genre-grid {
		display: grid;
		grid-template-columns: minmax(0, 1.65fr) minmax(280px, 0.85fr);
		gap: 18px;
		align-items: start;
	}
	.builder-main,
	.builder-side {
		display: flex;
		flex-direction: column;
		gap: 18px;
	}
	.builder-card {
		background: #0f1116;
		border: 1px solid var(--border);
		border-radius: 12px;
		padding: 18px;
	}
	.hero-card {
		padding-bottom: 14px;
	}
	.sticky-card {
		position: sticky;
		top: 24px;
	}
	.field-grid,
	.apply-grid {
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 12px;
	}
	.field {
		display: flex;
		flex-direction: column;
		gap: 6px;
	}
	.field-label {
		font-size: 11px;
		font-weight: 700;
		text-transform: uppercase;
		letter-spacing: 0.08em;
		color: var(--text-muted);
	}
	.lookup-row {
		display: grid;
		grid-template-columns: minmax(0, 1fr) auto;
		gap: 10px;
		margin-top: 12px;
	}
	input,
	select {
		width: 100%;
		box-sizing: border-box;
		padding: 10px 12px;
		border-radius: 10px;
		border: 1px solid var(--border);
		background: #12151b;
		color: var(--text);
		font-size: 13px;
		font-family: inherit;
	}
	input:focus,
	select:focus {
		outline: none;
		border-color: var(--border-strong);
	}
	.profile-note,
	.detail-message {
		margin: 12px 0 0;
		font-size: 12px;
		line-height: 1.5;
		color: var(--text-muted);
	}
	.result-list {
		display: flex;
		flex-direction: column;
		gap: 12px;
		margin-top: 12px;
	}
	.result-card {
		border: 1px solid var(--border);
		border-radius: 10px;
		background: #111419;
		padding: 14px;
	}
	.result-header,
	.detail-row,
	.genre-row {
		display: flex;
		align-items: flex-start;
		justify-content: space-between;
		gap: 12px;
	}
	.result-name {
		font-size: 14px;
		font-weight: 600;
		color: var(--text);
	}
	.result-meta,
	.genre-label {
		font-size: 12px;
		color: var(--text-muted);
	}
	.genre-value {
		font-size: 13px;
		color: var(--text);
		text-align: right;
	}
	.genre-panel {
		display: flex;
		flex-direction: column;
		gap: 10px;
		margin-top: 12px;
		padding-top: 12px;
		border-top: 1px solid var(--border);
	}
	.apply-actions {
		display: flex;
		align-items: flex-end;
	}
	.status {
		border-radius: 10px;
		padding: 10px 12px;
		font-size: 13px;
		background: rgba(255, 255, 255, 0.04);
		border: 1px solid var(--border);
	}
	.status.ok {
		border-color: rgba(34, 197, 94, 0.32);
		color: #b7f3ca;
	}
	.status.error {
		border-color: rgba(239, 68, 68, 0.32);
		color: #ffc2c2;
	}
	.inline-status {
		margin-top: 12px;
	}
	.empty-note {
		margin-top: 12px;
		font-size: 13px;
		color: var(--text-muted);
	}
	.btn {
		all: unset;
		cursor: pointer;
		display: inline-flex;
		align-items: center;
		justify-content: center;
		gap: 8px;
		padding: 10px 14px;
		border-radius: 10px;
		border: 1px solid var(--border);
		font-size: 13px;
		font-weight: 700;
	}
	.btn:disabled {
		opacity: 0.45;
		cursor: default;
	}
	.btn.ghost {
		background: #12151b;
		color: var(--text);
	}
	.btn.accent {
		background: linear-gradient(135deg, #2ad7ef 0%, #1798b4 100%);
		border-color: rgba(42, 215, 239, 0.35);
		color: #071217;
	}
	.btn.small {
		padding: 8px 12px;
		font-size: 12px;
	}
	@media (max-width: 1100px) {
		.genre-grid {
			grid-template-columns: 1fr;
		}
		.sticky-card {
			position: static;
		}
	}
	@media (max-width: 700px) {
		.page-header,
		.apply-grid,
		.field-grid {
			grid-template-columns: 1fr;
			display: grid;
		}
		.page-actions {
			width: 100%;
		}
		.lookup-row {
			grid-template-columns: 1fr;
		}
		.result-header,
		.detail-row,
		.genre-row {
			flex-direction: column;
		}
		.genre-value {
			text-align: left;
		}
	}
</style>
