<script lang="ts">
	import { logger } from '$lib/core/utils/logger';
	import { onDestroy, 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 { adbConnectionStatus } from '$lib/core/stores/adbConnectionStore';
	import { addNotification } from '$lib/core/stores/noti';
	import { materialFromServerQuery, referenceFromPage } from '$lib/core/stores/recipeStore';
	import { getRecipes } from '$lib/core/client/server';
	import type { Material } from '$lib/models/material.model';
	import { get } from 'svelte/store';
	import { permission } from '$lib/core/stores/permissions';
	import { departmentStore } from '$lib/core/stores/departments';

	const sourceDir = '/sdcard/coffeevending';
	const recipePaths = [`${sourceDir}/cfg/recipe_branch_dev.json`, `${sourceDir}/coffeethai02.json`];
	const machineCountryShortPath = '/mnt/sdcard/coffeevending/country/short';
	const machineCounterStrikeModePath = '/mnt/sdcard/coffeevending/enable_counter_strike_mode';
	const countryMaterialPrefixes: Record<string, number> = {
		aus: 51,
		gbr: 55,
		hkg: 54,
		ltu: 59,
		mys: 12,
		rou: 56,
		sgp: 52,
		tha: 0,
		uae_dubai: 53
	};

	type MaterialChannel =
		| 'BeanChannel'
		| 'PowderChannel'
		| 'SyrupChannel'
		| 'FreshSyrupChannel'
		| 'FrozenFruitChannel'
		| 'LeavesChannel'
		| 'SodaChannel'
		| 'ItemChannel'
		| 'IceScreamBingsuChannel'
		| 'Water'
		| 'Ice'
		| 'Cup'
		| 'Lid'
		| 'Straw'
		| 'Whipper'
		| 'VegetableForFry'
		| 'MeatForFry'
		| 'FruitForFry'
		| 'NoChannel';

	type MaterialChannelFlag =
		| '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;
	};

	const channelOptions: {
		value: MaterialChannel;
		label: string;
		typeId: number;
		channelFlag?: MaterialChannelFlag;
		canisterType: string;
		unit: string;
		ranges: { start: number; end: number; prefixable: boolean }[];
	}[] = [
		{
			value: 'BeanChannel',
			label: 'Bean',
			typeId: 1,
			channelFlag: 'BeanChannel',
			canisterType: 'BeanType',
			unit: 'refill=$bag,sum=#gram,rec=$gram',
			ranges: [
				{ start: 1001, end: 1009, prefixable: true },
				{ start: 1100, end: 1199, prefixable: true }
			]
		},
		{
			value: 'PowderChannel',
			label: 'Powder',
			typeId: 4,
			channelFlag: 'PowderChannel',
			canisterType: 'PowderType',
			unit: 'refill=$bag,sum=$gram,rec=$gram',
			ranges: [
				{ start: 1040, end: 1080, prefixable: true },
				{ start: 1300, end: 1399, prefixable: true }
			]
		},
		{
			value: 'SyrupChannel',
			label: 'Syrup',
			typeId: 2,
			channelFlag: 'SyrupChannel',
			canisterType: 'Bag In Box',
			unit: 'refill=$bag,sum=$gram,rec=$gram',
			ranges: [
				{ start: 1032, end: 1039, prefixable: true },
				{ start: 1020, end: 1030, prefixable: true },
				{ start: 1200, end: 1299, prefixable: true },
				{ start: 1400, end: 1499, prefixable: true }
			]
		},
		{
			value: 'FreshSyrupChannel',
			label: 'Fresh Syrup',
			typeId: 13,
			channelFlag: 'FreshSyrupChannel',
			canisterType: 'Tank',
			unit: 'refill=$bag,sum=$gram,rec=$gram',
			ranges: [{ start: 2201, end: 2299, prefixable: true }]
		},
		{
			value: 'FrozenFruitChannel',
			label: 'Frozen Fruit',
			typeId: 17,
			channelFlag: 'FrozenFruitChannel',
			canisterType: '',
			unit: 'refill=$L,sum=$ml,rec=$ml',
			ranges: [{ start: 2301, end: 2399, prefixable: true }]
		},
		{
			value: 'LeavesChannel',
			label: 'Leaves',
			typeId: 11,
			channelFlag: 'LeavesChannel',
			canisterType: '',
			unit: 'refill=$bag,sum=#gram,rec=$gram',
			ranges: [{ start: 1600, end: 1799, prefixable: true }]
		},
		{
			value: 'SodaChannel',
			label: 'Soda',
			typeId: 3,
			channelFlag: 'SodaChannel',
			canisterType: '',
			unit: 'refill=$L,sum=$ml,rec=$ml',
			ranges: [
				{ start: 1031, end: 1031, prefixable: false },
				{ start: 2400, end: 2499, prefixable: true }
			]
		},
		{
			value: 'ItemChannel',
			label: 'Item Box',
			typeId: 18,
			channelFlag: 'ItemChannel',
			canisterType: '',
			unit: 'refill=$cup,sum=$pcs,rec=$pcs',
			ranges: [{ start: 2501, end: 2699, prefixable: true }]
		},
		{
			value: 'IceScreamBingsuChannel',
			label: 'Machine / Ice Cream',
			typeId: 12,
			channelFlag: 'IceScreamBingsuChannel',
			canisterType: 'Machine',
			unit: 'refill=$bag,sum=$gram,rec=$gram',
			ranges: [{ start: 2100, end: 2199, prefixable: true }]
		},
		{
			value: 'Water',
			label: 'Water',
			typeId: 5,
			canisterType: '',
			unit: 'refill=$L,sum=$ml,rec=$ml',
			ranges: [{ start: 1, end: 1, prefixable: false }]
		},
		{
			value: 'Ice',
			label: 'Ice',
			typeId: 6,
			canisterType: '',
			unit: 'refill=$bag,sum=$gram,rec=$gram',
			ranges: [{ start: 9100, end: 9100, prefixable: false }]
		},
		{
			value: 'Cup',
			label: 'Cup',
			typeId: 7,
			canisterType: '',
			unit: 'refill=$cup,sum=$pcs,rec=$pcs',
			ranges: [{ start: 9500, end: 9549, prefixable: true }]
		},
		{
			value: 'Lid',
			label: 'Lid',
			typeId: 8,
			canisterType: '',
			unit: 'refill=$lid,sum=$pcs,rec=$pcs',
			ranges: [{ start: 9600, end: 9649, prefixable: true }]
		},
		{
			value: 'Straw',
			label: 'Straw',
			typeId: 9,
			canisterType: '',
			unit: 'refill=$straw,sum=$pcs,rec=$pcs',
			ranges: [{ start: 9700, end: 9749, prefixable: true }]
		},
		{
			value: 'Whipper',
			label: 'Whipper',
			typeId: 10,
			canisterType: 'Machine',
			unit: '',
			ranges: [
				{ start: 8102, end: 8102, prefixable: false },
				{ start: 8103, end: 8103, prefixable: false }
			]
		},
		{
			value: 'VegetableForFry',
			label: 'Vegetable For Fry',
			typeId: 14,
			canisterType: '',
			unit: '',
			ranges: []
		},
		{
			value: 'MeatForFry',
			label: 'Meat For Fry',
			typeId: 15,
			canisterType: '',
			unit: '',
			ranges: []
		},
		{
			value: 'FruitForFry',
			label: 'Fruit For Fry',
			typeId: 16,
			canisterType: '',
			unit: '',
			ranges: []
		},
		{
			value: 'NoChannel',
			label: 'Topping / No Channel',
			typeId: 0,
			canisterType: '',
			unit: '',
			ranges: [{ start: 8110, end: 8129, prefixable: false }]
		}
	];

	let devRecipe: any = $state(null);
	let loadedRecipePath = $state('');
	let loadedFromServer = $state(false);
	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 editingMaterialId: number | null = $state(null);
	let serverCountries: string[] = $state([]);
	let selectedServerCountry = $state(get(departmentStore) ?? '');
	let machineCountryShort = $state('');
	let machineCounterStrikeMode = $state(false);
	let serverCountryDialogOpen = $state(false);
	let machineNotConnectedDialogOpen = $state(false);
	let materialTypeDialogOpen = $state(false);

	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 isEditingMaterial = $derived(editingMaterialId != null);
	let duplicateMaterialOnCreate = $derived(!isEditingMaterial && existingMaterial != null);
	let activeMaterialCount = $derived(
		materials.filter((material) => (material.isUse as boolean) !== false).length
	);
	let canWriteToAndroid = $derived(!loadedFromServer && Boolean(adb.getAdbInstance()));
	let primaryLanguageLabel = $derived(getPrimaryLanguageLabel(getActiveRecipeCountry()));
	let materialListGridClass = 'md:grid-cols-[120px_minmax(0,1fr)_minmax(0,1fr)_150px_90px_150px]';
	let selectableMaterialTypeOptions = $derived(
		channelOptions
			.filter(
				(option) =>
					[1, 2, 4].includes(option.typeId) || (option.typeId === 12 && machineCounterStrikeMode)
			)
			.sort((a, b) => [1, 2, 4, 12].indexOf(a.typeId) - [1, 2, 4, 12].indexOf(b.typeId))
	);
	let channelSummary = $derived(
		channelOptions
			.map((option) => ({
				...option,
				count: materials.filter((material) => {
					if (!option.channelFlag) {
						return getChannelForMaterialId(Number(material.id))?.value === option.value;
					}
					return Boolean(material[option.channelFlag]);
				}).length
			}))
			.filter((option) => option.count > 0)
	);

	function getSupportedServerCountries(permissions: string[]) {
		return [
			...new Set(
				permissions
					.filter((item) => item.startsWith('document.read.'))
					.map((item) => item.split('.')[2])
					.filter(Boolean)
			)
		].sort();
	}

	function isThaiCountry(country: string) {
		return ['tha', 'thai'].includes(country.trim().toLowerCase());
	}

	function getActiveRecipeCountry() {
		return loadedFromServer ? selectedServerCountry : machineCountryShort;
	}

	function getPrimaryLanguageLabel(country: string) {
		const normalized = country.trim().toUpperCase();
		const languageByCountry: Record<string, string> = {
			THAI: 'Thai',
			THA: 'Thai',
			MYS: 'Malay',
			IDR: 'Indonesian',
			AUS: 'English',
			SGP: 'English',
			SG: 'English',
			UAE_DUBAI: 'Arabic',
			DUBAI: 'Arabic',
			HKG: 'Chinese',
			GBR: 'English',
			ROU: 'Romanian',
			LVA: 'Latvian',
			EST: 'Estonian',
			LTU: 'Lithuanian',
			USA_PEPSI: 'English'
		};

		return languageByCountry[normalized] ?? (normalized ? normalized : 'Primary');
	}

	function normalizeCountryShort(country: string) {
		return country.trim().toLowerCase();
	}

	function getCountryMaterialPrefix(country: string) {
		return countryMaterialPrefixes[normalizeCountryShort(country)] ?? 0;
	}

	function getActiveMaterialCountryPrefix() {
		return loadedFromServer
			? getCountryMaterialPrefix(selectedServerCountry)
			: getCountryMaterialPrefix(machineCountryShort);
	}

	const unsubscribePermission = permission.subscribe((permissions) => {
		serverCountries = getSupportedServerCountries(permissions);

		if (!selectedServerCountry && serverCountries.length > 0) {
			selectedServerCountry = serverCountries[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: ''
		};
	}

	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 loadMachineCountryShort() {
		const content = await pullTextWithRetry(machineCountryShortPath, 5000, 1);
		return normalizeCountryShort(content ?? '');
	}

	async function hasMachineCounterStrikeMode() {
		const content = await pullTextWithRetry(machineCounterStrikeModePath, 5000, 1);
		return content != undefined;
	}

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

			addNotification('INFO:Machine connected');
		} catch (error: any) {
			addNotification(`ERR:${error?.message ?? error}`);
		}
	}

	async function loadFromMachineSource() {
		if (!adb.getAdbInstance()) {
			machineNotConnectedDialogOpen = true;
			return;
		}

		await loadRecipeFromMachine();
	}

	async function waitForServerMaterials(timeoutMs = 20000, quietMs = 800) {
		const current = get(materialFromServerQuery);
		if (Array.isArray(current) && current.length > 0) {
			await new Promise((resolve) => setTimeout(resolve, quietMs));
			const latest = get(materialFromServerQuery);
			if (Array.isArray(latest) && latest.length === current.length) return latest;
		}

		return await new Promise<any[]>((resolve) => {
			let quietTimeout: ReturnType<typeof setTimeout> | undefined;
			const timeout = setTimeout(() => {
				if (quietTimeout) clearTimeout(quietTimeout);
				unsubscribe();
				const latest = get(materialFromServerQuery);
				resolve(Array.isArray(latest) ? latest : []);
			}, timeoutMs);

			const unsubscribe = materialFromServerQuery.subscribe((value) => {
				if (Array.isArray(value) && value.length > 0) {
					if (quietTimeout) clearTimeout(quietTimeout);
					quietTimeout = setTimeout(() => {
						clearTimeout(timeout);
						unsubscribe();
						resolve(value);
					}, quietMs);
				}
			});
		});
	}

	async function loadRecipeFromServer() {
		if (loading) return;
		if (!selectedServerCountry) {
			addNotification('ERR:Select country before loading from server');
			return;
		}

		loading = true;
		loadedFromServer = false;
		machineCountryShort = '';
		machineCounterStrikeMode = false;
		referenceFromPage.set('material');
		departmentStore.set(selectedServerCountry);
		materialFromServerQuery.set([]);
		try {
			await getRecipes();
			const serverMaterials = await waitForServerMaterials();
			if (serverMaterials.length === 0) {
				addNotification('ERR:Cannot fetch material data from server');
				return;
			}

			devRecipe = { MaterialSetting: serverMaterials };
			loadedRecipePath = `Server recipe (${selectedServerCountry})`;
			loadedFromServer = true;
			setNextAvailableId();
			addNotification('INFO:Material data loaded from server');
		} catch (error: any) {
			console.error('failed to load materials from server', error);
			addNotification(`ERR:Failed to load materials from server: ${error?.message ?? error}`);
		} finally {
			loading = false;
		}
	}

	function openServerCountryDialog() {
		if (serverCountries.length === 0) {
			addNotification('ERR:No readable countries');
			return;
		}

		serverCountryDialogOpen = true;
	}

	async function selectServerCountry(country: string) {
		selectedServerCountry = country;
		serverCountryDialogOpen = false;
		await loadRecipeFromServer();
	}

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

		loading = true;
		loadedFromServer = false;
		referenceFromPage.set('material');
		try {
			machineCountryShort = await loadMachineCountryShort();
			machineCounterStrikeMode = await hasMachineCounterStrikeMode();
			if (!machineCountryShort) addNotification('WARN:Cannot read machine country short');

			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) {
					logger.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 nextId = getNextAvailableIdForChannel(form.channel);
		form.id = nextId ?? getDefaultIdForChannel(form.channel) ?? form.id;
	}

	function getNextAvailableIdForChannel(channel: MaterialChannel) {
		const preset = channelOptions.find((option) => option.value === channel);
		if (!preset) return null;

		const usedIds = materials.map((material) => Number(material.id)).filter(Number.isFinite);
		const countryPrefix = getActiveMaterialCountryPrefix();
		for (const range of preset.ranges) {
			const { start, end, prefixable } = range;
			const usedRangeIds = usedIds
				.filter((id) => {
					if (countryPrefix > 0 && prefixable) return Math.floor(id / 10000) === countryPrefix;
					return id < 10000;
				})
				.map(getMaterialBaseId)
				.filter((id) => id >= start && id <= end);

			const nextBaseId = usedRangeIds.length > 0 ? Math.max(...usedRangeIds) + 1 : start;
			if (nextBaseId <= end) {
				return countryPrefix > 0 && prefixable ? countryPrefix * 10000 + nextBaseId : nextBaseId;
			}
		}

		return null;
	}

	function getMaterialBaseId(id: number) {
		if (!Number.isFinite(id)) return 0;
		return id >= 10000 ? id % 10000 : id;
	}

	function getDefaultIdForChannel(channel: MaterialChannel) {
		const preset = channelOptions.find((option) => option.value === channel);
		const firstRange = preset?.ranges[0];
		if (!firstRange) return null;

		const countryPrefix = getActiveMaterialCountryPrefix();
		return countryPrefix > 0 && firstRange.prefixable
			? countryPrefix * 10000 + firstRange.start
			: firstRange.start;
	}

	function getChannelForMaterialId(id: number) {
		const baseId = getMaterialBaseId(id);
		return channelOptions.find((option) =>
			option.ranges.some(({ start, end }) => baseId >= start && baseId <= end)
		);
	}

	function getRangeText(channel: MaterialChannel) {
		const preset = channelOptions.find((option) => option.value === channel);
		return preset?.ranges.map(({ start, end }) => `${start}-${end}`).join(', ') ?? '-';
	}

	function getDisplayRangeText(channel: MaterialChannel) {
		const preset = channelOptions.find((option) => option.value === channel);
		if (!preset) return '-';
		const countryPrefix = getActiveMaterialCountryPrefix();

		return preset.ranges
			.map(({ start, end, prefixable }) => {
				if (countryPrefix > 0 && prefixable) {
					return `${countryPrefix * 10000 + start}-${countryPrefix * 10000 + end}`;
				}
				return `${start}-${end}`;
			})
			.join(', ');
	}

	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;
		const idChannel = getChannelForMaterialId(Number(form.id));
		if (!idChannel || idChannel.value !== form.channel) setNextAvailableId();
	}

	function selectMaterialType(type: MaterialChannel) {
		const option = selectableMaterialTypeOptions.find((item) => item.value === type);
		if (!option) return;
		if (option.ranges.length === 0) {
			addNotification(`WARN:${option.label} has no material ID range defined yet`);
			return;
		}

		form.channel = type;
		applyChannelPreset();
		materialTypeDialogOpen = false;
	}

	function getSelectedMaterialTypeLabel() {
		const option = channelOptions.find((item) => item.value === form.channel);
		return option ? `TYPE ${option.typeId} - ${option.label}` : 'Select material type';
	}

	function buildMaterialSetting(): Material {
		const channelFlags = Object.fromEntries(
			channelOptions
				.filter((option) => option.channelFlag)
				.map((option) => [option.channelFlag, false])
		);
		const preset = channelOptions.find((option) => option.value === form.channel);
		if (preset?.channelFlag) channelFlags[preset.channelFlag] = 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: ['', '', '', '', '', '', '', ''],
			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 first';
		if (loadedFromServer) return 'Connect ADB before saving material changes';
		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 (duplicateMaterialOnCreate) return `Material ID ${form.id} already exists`;
		if (isEditingMaterial && Number(form.id) !== Number(editingMaterialId) && existingMaterial) {
			return `Material ID ${form.id} already exists`;
		}
		const idChannel = getChannelForMaterialId(Number(form.id));
		if (!idChannel || idChannel.value !== form.channel) {
			return `${form.channel} material ID must be in range ${getDisplayRangeText(form.channel)}`;
		}
		if (!form.materialName.trim()) return `${primaryLanguageLabel} material name is required`;
		if (!form.materialOtherName.trim()) return 'English material name is required';
		if (form.channel !== 'NoChannel' && !form.RawMaterialUnit.trim()) {
			return 'RawMaterialUnit is required';
		}
		return '';
	}

	async function persistRecipeToAndroid(nextRecipe: any) {
		if (!adb.getAdbInstance() || loadedFromServer) {
			throw new Error('ADB is required to save recipe changes to Android');
		}

		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(isEditingMaterial ? editingMaterialId : 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} ${isEditingMaterial ? 'updated' : 'created'} in ${targetPath}`
			);
			if (!isEditingMaterial) setNextAvailableId();
			editingMaterialId = null;
			showMaterialForm = false;
		} catch (error: any) {
			logger.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) => option.channelFlag && Boolean(material[option.channelFlag])
		);
		const inferredChannel = getChannelForMaterialId(Number(material.id));
		editingMaterialId = Number(material.id) || null;
		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 ?? inferredChannel?.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 ?? ''
		};
		showMaterialForm = true;
	}

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

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

	function closeMaterialForm() {
		showMaterialForm = false;
		editingMaterialId = null;
	}

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

	async function confirmDeleteMaterial() {
		if (!pendingDeleteMaterial) return;
		if (!devRecipe || !Array.isArray(devRecipe.MaterialSetting)) {
			addNotification('ERR:Load recipe first');
			return;
		}
		if (loadedFromServer) {
			addNotification('ERR:Connect ADB before deleting material');
			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) {
			logger.error('failed to delete material', error);
			addNotification(`ERR:Failed to delete material: ${error?.message ?? error}`);
		} finally {
			saving = false;
		}
	}

	onMount(() => {
		referenceFromPage.set('material');
	});

	onDestroy(() => {
		unsubscribePermission();
	});
</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 flex-col gap-2 rounded-xl border bg-background p-2 shadow-sm">
				<div class="px-2 text-xs font-medium tracking-wide text-muted-foreground uppercase">
					Recipe Source
				</div>
				<div class="flex flex-wrap gap-2">
					<Button
						variant="outline"
						size="sm"
						class="min-w-32 justify-center self-center text-xs font-medium text-muted-foreground"
						onclick={connectAdb}
						disabled={loading || saving}
					>
						<span
							class="h-2.5 w-2.5 rounded-full {adb.getAdbInstance()
								? 'bg-emerald-500'
								: 'bg-destructive'}"
						></span>
						{adb.getAdbInstance() ? 'Machine Connected' : 'Connect Machine'}
					</Button>
					<Button
						variant={!loadedFromServer && devRecipe ? 'default' : 'ghost'}
						class="min-w-36 justify-center px-5 py-5 text-base font-semibold"
						onclick={loadFromMachineSource}
						disabled={loading || saving}
					>
						{#if loading}
							<Spinner />
							Loading
						{:else}
							Machine
						{/if}
					</Button>
					<Button
						variant={loadedFromServer ? 'default' : 'ghost'}
						class="min-w-36 justify-center px-5 py-5 text-base font-semibold"
						onclick={openServerCountryDialog}
						disabled={loading || saving}
					>
						Server{selectedServerCountry ? `: ${selectedServerCountry}` : ''}
					</Button>
				</div>
			</div>
		</div>
	</div>

	<Dialog.Root bind:open={serverCountryDialogOpen}>
		<Dialog.Content class="sm:max-w-lg">
			<Dialog.Header>
				<Dialog.Title>Load Recipe From Server</Dialog.Title>
				<Dialog.Description>Select a country to load material data from server.</Dialog.Description>
			</Dialog.Header>

			<div class="grid gap-2 py-2">
				{#each serverCountries as country}
					<Button
						variant={country === selectedServerCountry ? 'default' : 'outline'}
						class="h-12 justify-start text-base font-semibold"
						onclick={() => selectServerCountry(country)}
						disabled={loading || saving}
					>
						{country}
					</Button>
				{/each}
			</div>

			<div class="flex justify-end">
				<Button variant="outline" onclick={() => (serverCountryDialogOpen = false)}>Cancel</Button>
			</div>
		</Dialog.Content>
	</Dialog.Root>

	<Dialog.Root bind:open={machineNotConnectedDialogOpen}>
		<Dialog.Content class="sm:max-w-md">
			<Dialog.Header>
				<Dialog.Title>Machine Not Connected</Dialog.Title>
				<Dialog.Description>
					Connect to the machine with ADB/WebUSB before loading recipe data from Machine.
				</Dialog.Description>
			</Dialog.Header>

			<div class="flex justify-end">
				<Button variant="outline" onclick={() => (machineNotConnectedDialogOpen = false)}>OK</Button
				>
			</div>
		</Dialog.Content>
	</Dialog.Root>

	<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>{isEditingMaterial ? 'Edit Material' : 'Add Material'}</Dialog.Title>
				<Dialog.Description>
					Create or update one <code>MaterialSetting</code> entry. Server-loaded data is read-only until
					ADB is connected.
				</Dialog.Description>
			</Dialog.Header>

			<div class="grid gap-6 lg:grid-cols-[minmax(0,1fr)_360px]">
				<Card.Root>
					<Card.Header>
						<Card.Title>{isEditingMaterial ? 'Edit Material' : 'Add Material'}</Card.Title>
					</Card.Header>
					<Card.Content class="grid gap-6">
						{#if duplicateMaterialOnCreate}
							<div
								class="rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive"
							>
								Material ID {form.id} already exists. Choose another ID before creating.
							</div>
						{:else if isEditingMaterial && Number(form.id) !== Number(editingMaterialId) && existingMaterial}
							<div
								class="rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive"
							>
								Material ID {form.id} already exists. Choose another ID before saving.
							</div>
						{:else if isEditingMaterial}
							<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"
							>
								Editing Material ID {editingMaterialId}.
							</div>
						{/if}

						<div class="grid gap-2">
							<Label>Material Type</Label>
							<Button
								type="button"
								variant="outline"
								class="h-auto min-h-12 justify-start px-3 py-2 text-left font-semibold"
								onclick={() => (materialTypeDialogOpen = true)}
							>
								<div class="grid gap-1">
									<span>{getSelectedMaterialTypeLabel()}</span>
								</div>
							</Button>
						</div>

						<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} readonly />
								<p class="text-xs text-muted-foreground">
									Generated from the highest existing ID in this type + 1.
								</p>
							</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">Name ({primaryLanguageLabel})</Label>
								<Input
									id="material-name"
									bind:value={form.materialName}
									placeholder={`Material name in ${primaryLanguageLabel}`}
								/>
							</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-2">
							<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-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={closeMaterialForm} disabled={saving}>Cancel</Button
							>
							<Button variant="outline" onclick={resetForm} disabled={saving}>Reset</Button>
							<Button
								onclick={saveMaterialToAndroid}
								disabled={!devRecipe ||
									loading ||
									saving ||
									!canWriteToAndroid ||
									duplicateMaterialOnCreate}
							>
								{saving ? 'Saving...' : isEditingMaterial ? '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>

	<Dialog.Root bind:open={materialTypeDialogOpen}>
		<Dialog.Content class="max-h-[90vh] overflow-y-auto sm:max-w-4xl">
			<Dialog.Header>
				<Dialog.Title>Select Material Type</Dialog.Title>
				<Dialog.Description>
					Choose the type for the new material. Material ID will be generated from existing IDs in
					that type + 1.
				</Dialog.Description>
			</Dialog.Header>

			<div class="grid gap-2 py-2 sm:grid-cols-2 xl:grid-cols-3">
				{#each selectableMaterialTypeOptions as option}
					<Button
						type="button"
						variant={form.channel === option.value ? 'default' : 'outline'}
						class="h-auto min-h-12 justify-start px-3 py-2 text-left"
						onclick={() => selectMaterialType(option.value)}
					>
						<div class="grid gap-1">
							<div class="font-semibold">TYPE {option.typeId} - {option.label}</div>
						</div>
					</Button>
				{/each}
			</div>

			<Dialog.Footer>
				<Button variant="outline" onclick={() => (materialTypeDialogOpen = false)}>Cancel</Button>
			</Dialog.Footer>
		</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. Connect ADB to
						write changes back to Android.
					</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...
					</div>
				{:else if !devRecipe}
					<div class="p-4 text-sm text-muted-foreground">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 {materialListGridClass} md:items-center"
					>
						<span>ID</span>
						<span>Name ({primaryLanguageLabel})</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 {materialListGridClass} 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 || !canWriteToAndroid}
									>
										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 || !canWriteToAndroid}
				>
					{saving ? 'Deleting...' : 'Delete Material'}
				</Button>
			</div>
		</Dialog.Content>
	</Dialog.Root>
</div>
