<script lang="ts">
	import { onMount } from 'svelte';
	import Button from '$lib/components/ui/button/button.svelte';
	import { Input } from '$lib/components/ui/input';
	import { Label } from '$lib/components/ui/label';
	import * as Card from '$lib/components/ui/card';
	import * as Dialog from '$lib/components/ui/dialog/index.js';
	import Checkbox from '$lib/components/ui/checkbox/checkbox.svelte';
	import Spinner from '$lib/components/ui/spinner/spinner.svelte';
	import * as adb from '$lib/core/adb/adb';
	import { addNotification } from '$lib/core/stores/noti';
	import { referenceFromPage } from '$lib/core/stores/recipeStore';
	import type { Material } from '$lib/models/material.model';

	const sourceDir = '/sdcard/coffeevending';
	const recipePaths = [`${sourceDir}/cfg/recipe_branch_dev.json`, `${sourceDir}/coffeethai02.json`];

	type MaterialChannel =
		| 'BeanChannel'
		| 'PowderChannel'
		| 'SyrupChannel'
		| 'FreshSyrupChannel'
		| 'FrozenFruitChannel'
		| 'LeavesChannel'
		| 'SodaChannel'
		| 'ItemChannel'
		| 'IceScreamBingsuChannel';

	type MaterialForm = {
		id: number;
		idAlternate: number;
		isUse: boolean;
		MaterialStatus: number;
		materialName: string;
		materialOtherName: string;
		MaterialDescrption: string;
		pathOtherName: string;
		CanisterType: string;
		channel: MaterialChannel;
		LowToOffline: number;
		AlarmIDWhenOffline: number;
		DrainTimer: number;
		ScheduleDrainType: number;
		pay_rettry_max_count: number;
		RawMaterialUnit: string;
		RefillUnitGram: boolean;
		RefillUnitMilliliters: boolean;
		RefillUnitPCS: boolean;
		IsEquipment: boolean;
		MaterialParameter: string;
		errorThai: string;
		errorEnglish: string;
	};

	const channelOptions: {
		value: MaterialChannel;
		label: string;
		canisterType: string;
		unit: string;
	}[] = [
		{
			value: 'BeanChannel',
			label: 'Bean',
			canisterType: 'BeanType',
			unit: 'refill=$bag,sum=#gram,rec=$gram'
		},
		{
			value: 'PowderChannel',
			label: 'Powder',
			canisterType: 'PowderType',
			unit: 'refill=$bag,sum=$gram,rec=$gram'
		},
		{
			value: 'SyrupChannel',
			label: 'Syrup',
			canisterType: 'Bag In Box',
			unit: 'refill=$bag,sum=$gram,rec=$gram'
		},
		{
			value: 'FreshSyrupChannel',
			label: 'Fresh Syrup',
			canisterType: 'Tank',
			unit: 'refill=$bag,sum=$gram,rec=$gram'
		},
		{
			value: 'FrozenFruitChannel',
			label: 'Frozen Fruit',
			canisterType: '',
			unit: 'refill=$L,sum=$ml,rec=$ml'
		},
		{
			value: 'LeavesChannel',
			label: 'Leaves',
			canisterType: '',
			unit: 'refill=$bag,sum=#gram,rec=$gram'
		},
		{
			value: 'SodaChannel',
			label: 'Soda',
			canisterType: '',
			unit: 'refill=$L,sum=$ml,rec=$ml'
		},
		{
			value: 'ItemChannel',
			label: 'Item',
			canisterType: '',
			unit: 'refill=$cup,sum=$pcs,rec=$pcs'
		},
		{
			value: 'IceScreamBingsuChannel',
			label: 'Machine / Ice Cream',
			canisterType: 'Machine',
			unit: 'refill=$bag,sum=$gram,rec=$gram'
		}
	];

	let devRecipe: any = $state(null);
	let loadedRecipePath = $state('');
	let loading = $state(false);
	let saving = $state(false);
	let search = $state('');
	let showMaterialForm = $state(false);
	let deleteConfirmOpen = $state(false);
	let pendingDeleteMaterial: Material | null = $state(null);

	let form: MaterialForm = $state(createInitialForm());

	let materials = $derived<Material[]>(devRecipe?.MaterialSetting ?? []);
	let filteredMaterials = $derived(
		materials.filter((material) => {
			const text =
				`${material.id} ${material.materialName ?? ''} ${material.materialOtherName ?? ''} ${material.pathOtherName ?? ''}`.toLowerCase();
			return text.includes(search.toLowerCase());
		})
	);
	let materialPreview = $derived(buildMaterialSetting());
	let previewJson = $derived(JSON.stringify(materialPreview, null, 2));
	let existingMaterial = $derived(
		materials.find((material) => Number(material.id) === Number(form.id)) ?? null
	);
	let activeMaterialCount = $derived(
		materials.filter((material) => (material.isUse as boolean) !== false).length
	);
	let channelSummary = $derived(
		channelOptions
			.map((option) => ({
				...option,
				count: materials.filter((material) => Boolean(material[option.value])).length
			}))
			.filter((option) => option.count > 0)
	);

	function createInitialForm(): MaterialForm {
		return {
			id: 1001,
			idAlternate: 0,
			isUse: true,
			MaterialStatus: 0,
			materialName: '',
			materialOtherName: '',
			MaterialDescrption: '',
			pathOtherName: 'Bean box',
			CanisterType: 'BeanType',
			channel: 'BeanChannel',
			LowToOffline: 30,
			AlarmIDWhenOffline: 0,
			DrainTimer: 0,
			ScheduleDrainType: 0,
			pay_rettry_max_count: 0,
			RawMaterialUnit: 'refill=$bag,sum=#gram,rec=$gram',
			RefillUnitGram: false,
			RefillUnitMilliliters: false,
			RefillUnitPCS: false,
			IsEquipment: false,
			MaterialParameter: '',
			errorThai: '',
			errorEnglish: ''
		};
	}

	async function pullTextWithRetry(path: string, timeoutMs = 15000, attempts = 2) {
		for (let attempt = 1; attempt <= attempts; attempt++) {
			const content = await adb.pull(path, timeoutMs);
			if (content != undefined) return content;
			if (attempt < attempts) await new Promise((resolve) => setTimeout(resolve, 500 * attempt));
		}
	}

	async function connectAdb() {
		try {
			if (!adb.getAdbInstance()) {
				if (!('usb' in navigator)) throw new Error('WebUSB not supported');
				await adb.connectRecipeMenuViaWebUSB();
			}

			await loadRecipeFromMachine();
		} catch (error: any) {
			addNotification(`ERR:${error?.message ?? error}`);
		}
	}

	async function loadRecipeFromMachine() {
		if (loading) return;
		if (!adb.getAdbInstance()) {
			addNotification('ERR:ADB is not connected');
			return;
		}

		loading = true;
		referenceFromPage.set('material');
		try {
			for (const recipePath of recipePaths) {
				const content = await pullTextWithRetry(recipePath);
				if (!content || content.trim().length === 0) continue;

				try {
					devRecipe = JSON.parse(content);
					loadedRecipePath = recipePath;
					setNextAvailableId();
					addNotification(`INFO:Recipe loaded from ${recipePath}`);
					return;
				} catch (error) {
					console.error('failed to parse recipe json', recipePath, error);
					addNotification(`ERR:Invalid recipe JSON from ${recipePath}`);
				}
			}

			addNotification('ERR:Cannot fetch recipe from machine');
		} finally {
			loading = false;
		}
	}

	function setNextAvailableId() {
		const usedIds = new Set(materials.map((material) => Number(material.id)));
		let nextId = Math.max(1001, ...[...usedIds].filter(Number.isFinite)) + 1;
		while (usedIds.has(nextId)) nextId++;
		form.id = nextId;
	}

	function applyChannelPreset() {
		const preset = channelOptions.find((option) => option.value === form.channel);
		if (!preset) return;

		form.CanisterType = preset.canisterType;
		form.RawMaterialUnit = preset.unit;
		if (!form.pathOtherName) form.pathOtherName = preset.label;
	}

	function buildMaterialSetting(): Material {
		const channelFlags = Object.fromEntries(channelOptions.map((option) => [option.value, false]));
		channelFlags[form.channel] = true;

		return {
			AlarmIDWhenOffline: Number(form.AlarmIDWhenOffline) || 0,
			BeanChannel: Boolean(channelFlags.BeanChannel),
			CanisterType: form.CanisterType,
			DrainTimer: Number(form.DrainTimer) || 0,
			FreshSyrupChannel: Boolean(channelFlags.FreshSyrupChannel),
			FrozenFruitChannel: Boolean(channelFlags.FrozenFruitChannel),
			IceScreamBingsuChannel: Boolean(channelFlags.IceScreamBingsuChannel),
			IsEquipment: form.IsEquipment,
			ItemChannel: Boolean(channelFlags.ItemChannel),
			LeavesChannel: Boolean(channelFlags.LeavesChannel),
			LowToOffline: Number(form.LowToOffline) || 0,
			MaterialDescrption: form.MaterialDescrption,
			MaterialDescription: form.MaterialDescrption,
			MaterialStatus: Number(form.MaterialStatus) || 0,
			PowderChannel: Boolean(channelFlags.PowderChannel),
			RefillUnitGram: form.RefillUnitGram,
			RefillUnitMilliliters: form.RefillUnitMilliliters,
			RefillUnitPCS: form.RefillUnitPCS,
			ScheduleDrainType: Number(form.ScheduleDrainType) || 0,
			SodaChannel: Boolean(channelFlags.SodaChannel),
			StrTextShowError: [form.errorThai, form.errorEnglish, '', '', '', '', '', ''],
			SyrupChannel: Boolean(channelFlags.SyrupChannel),
			id: Number(form.id) || 0,
			idAlternate: Number(form.idAlternate) || 0,
			isUse: form.isUse,
			materialOtherName: form.materialOtherName,
			materialName: form.materialName,
			pathOtherName: form.pathOtherName,
			pay_rettry_max_count: Number(form.pay_rettry_max_count) || 0,
			RawMaterialUnit: form.RawMaterialUnit,
			MaterialParameter: form.MaterialParameter
		} as Material;
	}

	function validateMaterial() {
		if (!devRecipe) return 'Load recipe from Android first';
		if (!Array.isArray(devRecipe.MaterialSetting)) return 'Recipe has no MaterialSetting array';
		if (!Number.isFinite(Number(form.id)) || Number(form.id) <= 0) return 'Material ID is required';
		if (!form.materialName.trim()) return 'Thai material name is required';
		if (!form.materialOtherName.trim()) return 'English material name is required';
		if (!form.RawMaterialUnit.trim()) return 'RawMaterialUnit is required';
		return '';
	}

	async function persistRecipeToAndroid(nextRecipe: any) {
		nextRecipe.Timestamp = new Date().toLocaleString('en-GB', {
			day: '2-digit',
			month: 'short',
			year: 'numeric',
			hour: '2-digit',
			minute: '2-digit',
			second: '2-digit',
			hour12: false
		});

		const targetPath = loadedRecipePath || recipePaths[0];
		const tempPath = `${targetPath}.tmp`;
		await adb.push(tempPath, JSON.stringify(nextRecipe, null, 2));
		const result = await adb.executeCmd(`mv ${tempPath} ${targetPath}`);
		if (result?.error) throw new Error(String(result.error));

		devRecipe = nextRecipe;
		return targetPath;
	}

	async function saveMaterialToAndroid() {
		const error = validateMaterial();
		if (error) {
			addNotification(`ERR:${error}`);
			return;
		}

		saving = true;
		try {
			const material = buildMaterialSetting();
			const nextRecipe = JSON.parse(JSON.stringify(devRecipe));
			const materialIndex = nextRecipe.MaterialSetting.findIndex(
				(item: Material) => Number(item.id) === Number(material.id)
			);

			if (materialIndex >= 0) {
				nextRecipe.MaterialSetting[materialIndex] = {
					...nextRecipe.MaterialSetting[materialIndex],
					...material
				};
			} else {
				nextRecipe.MaterialSetting.push(material);
			}

			const targetPath = await persistRecipeToAndroid(nextRecipe);
			addNotification(
				`INFO:Material ${material.id} ${materialIndex >= 0 ? 'updated' : 'created'} in ${targetPath}`
			);
			if (materialIndex < 0) setNextAvailableId();
			showMaterialForm = false;
		} catch (error: any) {
			console.error('failed to save material', error);
			addNotification(`ERR:Failed to save material: ${error?.message ?? error}`);
		} finally {
			saving = false;
		}
	}

	function loadMaterialIntoForm(material: Material) {
		const activeChannel = channelOptions.find((option) => Boolean(material[option.value]));
		form = {
			...createInitialForm(),
			id: Number(material.id) || 0,
			idAlternate: Number(material.idAlternate) || 0,
			isUse: (material.isUse as boolean) !== false,
			MaterialStatus: Number(material.MaterialStatus) || 0,
			materialName: material.materialName ?? '',
			materialOtherName: material.materialOtherName ?? '',
			MaterialDescrption: material.MaterialDescrption ?? material.MaterialDescription ?? '',
			pathOtherName: material.pathOtherName ?? '',
			CanisterType: material.CanisterType ?? '',
			channel: activeChannel?.value ?? 'BeanChannel',
			LowToOffline: Number(material.LowToOffline) || 0,
			AlarmIDWhenOffline: Number(material.AlarmIDWhenOffline) || 0,
			DrainTimer: Number(material.DrainTimer) || 0,
			ScheduleDrainType: Number(material.ScheduleDrainType) || 0,
			pay_rettry_max_count: Number(material.pay_rettry_max_count) || 0,
			RawMaterialUnit: material.RawMaterialUnit ?? '',
			RefillUnitGram: Boolean(material.RefillUnitGram),
			RefillUnitMilliliters: Boolean(material.RefillUnitMilliliters),
			RefillUnitPCS: Boolean(material.RefillUnitPCS),
			IsEquipment: Boolean(material.IsEquipment),
			MaterialParameter: material.MaterialParameter ?? '',
			errorThai: material.StrTextShowError?.[0] ?? '',
			errorEnglish: material.StrTextShowError?.[1] ?? ''
		};
		showMaterialForm = true;
	}

	function resetForm() {
		form = createInitialForm();
		if (devRecipe) setNextAvailableId();
	}

	function openAddMaterialForm() {
		resetForm();
		showMaterialForm = true;
	}

	function openDeleteConfirm(material: Material) {
		pendingDeleteMaterial = material;
		deleteConfirmOpen = true;
	}

	async function confirmDeleteMaterial() {
		if (!pendingDeleteMaterial) return;
		if (!devRecipe || !Array.isArray(devRecipe.MaterialSetting)) {
			addNotification('ERR:Load recipe from Android first');
			return;
		}

		saving = true;
		try {
			const materialId = Number(pendingDeleteMaterial.id);
			const nextRecipe = JSON.parse(JSON.stringify(devRecipe));
			const beforeCount = nextRecipe.MaterialSetting.length;
			nextRecipe.MaterialSetting = nextRecipe.MaterialSetting.filter(
				(material: Material) => Number(material.id) !== materialId
			);

			if (nextRecipe.MaterialSetting.length === beforeCount) {
				addNotification(`WARN:Material not found: ${materialId}`);
				return;
			}

			await persistRecipeToAndroid(nextRecipe);
			addNotification(`INFO:Material deleted: ${materialId}`);
			deleteConfirmOpen = false;
			pendingDeleteMaterial = null;
		} catch (error: any) {
			console.error('failed to delete material', error);
			addNotification(`ERR:Failed to delete material: ${error?.message ?? error}`);
		} finally {
			saving = false;
		}
	}

	onMount(() => {
		referenceFromPage.set('material');
		if (adb.getAdbInstance()) void loadRecipeFromMachine();
	});
</script>

<div class="mx-auto flex w-full max-w-7xl flex-col gap-6 p-8">
	<div class="overflow-hidden rounded-2xl border bg-card shadow-sm">
		<div
			class="flex flex-col gap-5 bg-gradient-to-br from-muted/70 via-card to-card p-6 md:flex-row md:items-end md:justify-between"
		>
			<div>
				<p class="text-xs font-semibold tracking-[0.2em] text-muted-foreground uppercase">
					Android Recipe
				</p>
				<h1 class="mt-2 text-4xl font-bold tracking-tight">Material Setting</h1>
				<!-- <p class="mt-2 max-w-2xl text-sm text-muted-foreground">
					Browse existing Android <code>MaterialSetting</code> entries first, then add or edit a material
					in a dialog without leaving this list.
				</p> -->
				{#if loadedRecipePath}
					<p class="mt-3 font-mono text-xs text-muted-foreground">Loaded: {loadedRecipePath}</p>
				{/if}
			</div>

			<div class="flex gap-2">
				<Button variant="outline" onclick={connectAdb} disabled={loading || saving}>
					{#if loading}
						<Spinner />
						Loading
					{:else if devRecipe}
						Reload From Android
					{:else}
						Connect & Load
					{/if}
				</Button>
			</div>
		</div>
	</div>

	<div class="grid gap-2 md:grid-cols-3">
		<div class="rounded-lg border border-sky-200 bg-card px-4 py-3 shadow-sm dark:border-sky-900">
			<div class="mb-2 h-1 w-10 rounded-full bg-sky-500"></div>
			<div class="text-xs text-muted-foreground">Total materials</div>
			<div class="mt-1 text-xl font-semibold">{materials.length}</div>
		</div>
		<div
			class="rounded-lg border border-emerald-200 bg-card px-4 py-3 shadow-sm dark:border-emerald-900"
		>
			<div class="mb-2 h-1 w-10 rounded-full bg-emerald-500"></div>
			<div class="text-xs text-muted-foreground">Active materials</div>
			<div class="mt-1 text-xl font-semibold">{activeMaterialCount}</div>
		</div>
		<div
			class="rounded-lg border border-violet-200 bg-card px-4 py-3 shadow-sm dark:border-violet-900"
		>
			<div class="mb-2 h-1 w-10 rounded-full bg-violet-500"></div>
			<div class="text-xs text-muted-foreground">Channels in use</div>
			<div class="mt-1 text-xl font-semibold">{channelSummary.length}</div>
		</div>
	</div>

	<Dialog.Root bind:open={showMaterialForm}>
		<Dialog.Content class="max-h-[92vh] overflow-y-auto sm:max-w-6xl">
			<Dialog.Header>
				<Dialog.Title>{existingMaterial ? 'Edit Material' : 'Add Material'}</Dialog.Title>
				<Dialog.Description>
					Create or update one <code>MaterialSetting</code> entry. The JSON preview shows the payload
					before saving to Android.
				</Dialog.Description>
			</Dialog.Header>

			<div class="grid gap-6 lg:grid-cols-[minmax(0,1fr)_360px]">
				<Card.Root>
					<Card.Header>
						<Card.Title>{existingMaterial ? 'Edit Material' : 'Add Material'}</Card.Title>
					</Card.Header>
					<Card.Content class="grid gap-6">
						{#if existingMaterial}
							<div
								class="rounded-md border border-amber-300 bg-amber-50 p-3 text-sm text-amber-900 dark:border-amber-900 dark:bg-amber-950 dark:text-amber-200"
							>
								Material ID {form.id} already exists. Saving will update this MaterialSetting.
							</div>
						{/if}

						<div class="grid gap-4 md:grid-cols-3">
							<div class="grid gap-2">
								<Label for="material-id">Material ID</Label>
								<Input id="material-id" type="number" bind:value={form.id} />
							</div>
							<div class="grid gap-2">
								<Label for="material-id-alt">Alternate ID</Label>
								<Input id="material-id-alt" type="number" bind:value={form.idAlternate} />
							</div>
							<div class="grid gap-2">
								<Label for="material-status">Status</Label>
								<select
									id="material-status"
									class="h-9 rounded-md border bg-background px-3 text-sm"
									bind:value={form.MaterialStatus}
								>
									<option value={0}>0 - Ready</option>
									<option value={2}>2 - Obsolete</option>
									<option value={11}>11 - Pending online</option>
									<option value={12}>12 - Pending offline</option>
								</select>
							</div>
						</div>

						<div class="grid gap-4 md:grid-cols-2">
							<div class="grid gap-2">
								<Label for="material-name">Thai Name</Label>
								<Input
									id="material-name"
									bind:value={form.materialName}
									placeholder="เช่น กาแฟคั่วเข้ม"
								/>
							</div>
							<div class="grid gap-2">
								<Label for="material-other-name">English Name</Label>
								<Input
									id="material-other-name"
									bind:value={form.materialOtherName}
									placeholder="e.g. dark-roasts"
								/>
							</div>
						</div>

						<div class="grid gap-4 md:grid-cols-2">
							<div class="grid gap-2">
								<Label for="material-description">Description</Label>
								<Input
									id="material-description"
									bind:value={form.MaterialDescrption}
									placeholder="e.g. Barista Blend"
								/>
							</div>
							<div class="grid gap-2">
								<Label for="path-other-name">Path / Canister Name</Label>
								<Input
									id="path-other-name"
									bind:value={form.pathOtherName}
									placeholder="e.g. Bean box"
								/>
							</div>
						</div>

						<div class="grid gap-4 md:grid-cols-3">
							<div class="grid gap-2">
								<Label for="channel">Channel</Label>
								<select
									id="channel"
									class="h-9 rounded-md border bg-background px-3 text-sm"
									value={form.channel}
									onchange={(event) => {
										form.channel = event.currentTarget.value as MaterialChannel;
										applyChannelPreset();
									}}
								>
									{#each channelOptions as option}
										<option value={option.value}>{option.label}</option>
									{/each}
								</select>
							</div>
							<div class="grid gap-2">
								<Label for="canister-type">Canister Type</Label>
								<Input id="canister-type" bind:value={form.CanisterType} />
							</div>
							<div class="grid gap-2">
								<Label for="low-to-offline">Low To Offline</Label>
								<Input id="low-to-offline" type="number" bind:value={form.LowToOffline} />
							</div>
						</div>

						<div class="grid gap-2">
							<Label for="raw-material-unit">RawMaterialUnit</Label>
							<Input id="raw-material-unit" bind:value={form.RawMaterialUnit} />
							<p class="text-xs text-muted-foreground">
								Examples: <code>refill=$bag,sum=$gram,rec=$gram</code>,
								<code>refill=$L,sum=$ml,rec=$ml</code>
							</p>
						</div>

						<div class="grid gap-4 md:grid-cols-2">
							<div class="grid gap-2">
								<Label for="error-thai">Error Text TH</Label>
								<Input id="error-thai" bind:value={form.errorThai} placeholder="เช่น กาแฟหมด" />
							</div>
							<div class="grid gap-2">
								<Label for="error-english">Error Text EN</Label>
								<Input
									id="error-english"
									bind:value={form.errorEnglish}
									placeholder="e.g. Out of Coffee bean"
								/>
							</div>
						</div>

						<div class="grid gap-4 md:grid-cols-4">
							<div class="grid gap-2">
								<Label for="alarm-id">Alarm ID</Label>
								<Input id="alarm-id" type="number" bind:value={form.AlarmIDWhenOffline} />
							</div>
							<div class="grid gap-2">
								<Label for="drain-timer">Drain Timer</Label>
								<Input id="drain-timer" type="number" bind:value={form.DrainTimer} />
							</div>
							<div class="grid gap-2">
								<Label for="schedule-drain">Schedule Drain</Label>
								<Input id="schedule-drain" type="number" bind:value={form.ScheduleDrainType} />
							</div>
							<div class="grid gap-2">
								<Label for="pay-retry">Pay Retry Max</Label>
								<Input id="pay-retry" type="number" bind:value={form.pay_rettry_max_count} />
							</div>
						</div>

						<div class="grid gap-3 rounded-md border p-4 md:grid-cols-5">
							<label class="flex items-center gap-2 text-sm">
								<Checkbox bind:checked={form.isUse} />
								Use material
							</label>
							<label class="flex items-center gap-2 text-sm">
								<Checkbox bind:checked={form.IsEquipment} />
								Equipment
							</label>
							<label class="flex items-center gap-2 text-sm">
								<Checkbox bind:checked={form.RefillUnitGram} />
								Refill gram
							</label>
							<label class="flex items-center gap-2 text-sm">
								<Checkbox bind:checked={form.RefillUnitMilliliters} />
								Refill ml
							</label>
							<label class="flex items-center gap-2 text-sm">
								<Checkbox bind:checked={form.RefillUnitPCS} />
								Refill pcs
							</label>
						</div>

						<div class="grid gap-2">
							<Label for="material-parameter">MaterialParameter</Label>
							<textarea
								id="material-parameter"
								class="min-h-20 rounded-md border bg-background px-3 py-2 font-mono text-sm"
								bind:value={form.MaterialParameter}
								placeholder="Optional extra Android parameter"
							></textarea>
						</div>

						<div class="flex justify-end gap-2">
							<Button variant="outline" onclick={() => (showMaterialForm = false)} disabled={saving}
								>Cancel</Button
							>
							<Button variant="outline" onclick={resetForm} disabled={saving}>Reset</Button>
							<Button onclick={saveMaterialToAndroid} disabled={!devRecipe || loading || saving}>
								{saving ? 'Saving...' : existingMaterial ? 'Update Material' : 'Create Material'}
							</Button>
						</div>
					</Card.Content>
				</Card.Root>

				<Card.Root>
					<Card.Header>
						<Card.Title>Preview JSON</Card.Title>
						<Card.Description>
							Payload that will be upserted into <code>MaterialSetting</code>.
						</Card.Description>
					</Card.Header>
					<Card.Content>
						<pre
							class="max-h-[520px] overflow-auto rounded-md bg-muted p-4 text-xs">{previewJson}</pre>
					</Card.Content>
				</Card.Root>
			</div>
		</Dialog.Content>
	</Dialog.Root>

	<Card.Root>
		<Card.Header>
			<div class="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
				<div>
					<Card.Title>Existing Materials</Card.Title>
					<Card.Description>
						Use Edit to update a material, or Delete to remove it after confirmation.
					</Card.Description>
				</div>
				<Button onclick={openAddMaterialForm} disabled={!devRecipe || loading || saving}
					>Add Material</Button
				>
			</div>
		</Card.Header>
		<Card.Content class="grid gap-4">
			<div class="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
				<Input class="lg:max-w-md" bind:value={search} placeholder="Search by id, name, path" />
				<div class="flex flex-wrap gap-2">
					{#each channelSummary as channel}
						<span class="rounded-full border bg-muted/40 px-3 py-1 text-xs text-muted-foreground">
							{channel.label}: {channel.count}
						</span>
					{/each}
				</div>
			</div>
			<div class="overflow-hidden rounded-md border">
				{#if loading}
					<div class="flex items-center gap-3 p-4 text-sm text-muted-foreground">
						<Spinner />
						Loading materials from Android...
					</div>
				{:else if !devRecipe}
					<div class="p-4 text-sm text-muted-foreground">Connect and load recipe first.</div>
				{:else if filteredMaterials.length === 0}
					<div class="p-4 text-sm text-muted-foreground">No materials found.</div>
				{:else}
					<div
						class="hidden border-b bg-muted/50 px-4 py-2 text-xs font-medium text-muted-foreground md:grid md:grid-cols-[120px_minmax(0,1fr)_minmax(0,1fr)_150px_90px_150px] md:items-center"
					>
						<span>ID</span>
						<span>Thai Name</span>
						<span>English Name</span>
						<span>Path</span>
						<span>Use</span>
						<span class="text-right">Actions</span>
					</div>
					<div class="grid max-h-[70vh] overflow-auto">
						{#each filteredMaterials as material}
							<div
								class="grid w-full gap-3 border-b p-4 text-sm transition-colors hover:bg-primary/5 md:grid-cols-[120px_minmax(0,1fr)_minmax(0,1fr)_150px_90px_150px] md:items-center"
							>
								<span class="font-mono font-medium text-primary">{material.id}</span>
								<span class="font-medium">{material.materialName || '-'}</span>
								<span class="text-muted-foreground">{material.materialOtherName || '-'}</span>
								<span class="text-muted-foreground">{material.pathOtherName || '-'}</span>
								<span
									class="w-fit rounded-full px-2.5 py-1 text-xs {(material.isUse as boolean) !==
									false
										? 'bg-emerald-100 text-emerald-700 dark:bg-emerald-950 dark:text-emerald-300'
										: 'bg-amber-100 text-amber-800 dark:bg-amber-950 dark:text-amber-300'}"
								>
									{(material.isUse as boolean) !== false ? 'Use' : 'Not use'}
								</span>
								<div class="flex gap-2 md:justify-end">
									<Button
										variant="outline"
										size="sm"
										onclick={() => loadMaterialIntoForm(material)}
									>
										Edit
									</Button>
									<Button
										variant="destructive"
										size="sm"
										onclick={() => openDeleteConfirm(material)}
										disabled={saving}
									>
										Delete
									</Button>
								</div>
							</div>
						{/each}
					</div>
				{/if}
			</div>
		</Card.Content>
	</Card.Root>

	<Dialog.Root bind:open={deleteConfirmOpen}>
		<Dialog.Content class="sm:max-w-md">
			<Dialog.Header>
				<Dialog.Title>Delete Material?</Dialog.Title>
				<Dialog.Description>
					This will remove the material from <code>MaterialSetting</code> in the Android recipe JSON.
				</Dialog.Description>
			</Dialog.Header>

			{#if pendingDeleteMaterial}
				<div class="rounded-md border bg-muted/30 p-4 text-sm">
					<div class="text-xs text-muted-foreground">Material</div>
					<div class="mt-1 font-mono font-medium">{pendingDeleteMaterial.id}</div>
					<div class="mt-1 font-medium">
						{pendingDeleteMaterial.materialName ||
							pendingDeleteMaterial.materialOtherName ||
							'Unnamed'}
					</div>
				</div>
			{/if}

			<div class="flex justify-end gap-2">
				<Button
					variant="outline"
					onclick={() => {
						deleteConfirmOpen = false;
						pendingDeleteMaterial = null;
					}}
					disabled={saving}
				>
					Cancel
				</Button>
				<Button variant="destructive" onclick={confirmDeleteMaterial} disabled={saving}>
					{saving ? 'Deleting...' : 'Delete Material'}
				</Button>
			</div>
		</Dialog.Content>
	</Dialog.Root>
</div>
