<script lang="ts">
	import Button from '$lib/components/ui/button/button.svelte';
	import Spinner from '$lib/components/ui/spinner/spinner.svelte';
	import { onMount } from 'svelte';

	import * as adb from '$lib/core/adb/adb';
	import { addNotification } from '$lib/core/stores/noti';
	import { columns, type RecipeOverview } from '../../recipe/overview/columns';
	import {
		materialFromMachineQuery,
		recipeFromMachine,
		recipeFromMachineLoading,
		recipeFromMachineQuery,
		toppingListFromMachineQuery,
		referenceFromPage,
		toppingGroupFromMachineQuery
	} from '$lib/core/stores/recipeStore';
	import DataTable from '../../recipe/overview/data-table.svelte';
	import { handleIncomingMessages } from '$lib/core/handlers/messageHandler';
	import { auth as authStore } from '$lib/core/stores/auth';
	import { machineInfoStore } from '$lib/core/stores/machineInfoStore';
	import { get } from 'svelte/store';
	import {
		AdbDaemonWebUsbDevice,
		AdbDaemonWebUsbDeviceManager
	} from '@yume-chan/adb-daemon-webusb';
	import AdbWebCredentialStore from '@yume-chan/adb-credential-web';
	import { deviceCredentialManager } from '$lib/core/adb/deviceCredManager';
	import { afterNavigate } from '$app/navigation';
	import { env } from '$env/dynamic/public';
	import { fade } from 'svelte/transition';

	const sourceDir = '/sdcard/coffeevending';

	// fetched recipe
	let devRecipe: any | undefined = $state();

	// data to display
	let data: { recipes: RecipeOverview[] } = $state({
		recipes: []
	});

	let brew_status: string = $state('');

	// refresh command
	let refresh_counter: number = $state(0);

	async function startFetchRecipeFromMachine() {
		let instance = adb.getAdbInstance();
		// recipeFromMachineLoading.set(true);
		referenceFromPage.set('brew');
		console.log('check instance', instance);
		if (instance) {
			console.log('instance passed!');
			let dev_recipe = await adb.pull(`${sourceDir}/cfg/recipe_branch_dev.json`);
			console.log('dev recipe ok', dev_recipe != undefined, dev_recipe);
			if (dev_recipe) {
				if (dev_recipe.length == 0) {
					// case error, do last retry
					dev_recipe = await adb.pull(`${sourceDir}/coffeethai02.json`);

					if (dev_recipe && dev_recipe.length == 0)
						addNotification('ERROR:Cannot fetch recipe from machine');
					else if (dev_recipe) {
						// From coffeethai02
						devRecipe = JSON.parse(dev_recipe);
						// recipeFromMachineLoading.set(false);
						addNotification('INFO:Fetch recipe success!');

						buildOverviewForBrewing();
					}
				} else {
					// from recipe_branch_dev
					devRecipe = JSON.parse(dev_recipe);
					// recipeFromMachineLoading.set(false);
					// addNotification('INFO:Fetch recipe success!');

					buildOverviewForBrewing();
				}
			}
		} else {
			addNotification('ERROR:Cannot connect to machine');
			// recipeFromMachineLoading.set(false);
		}
	}

	async function loadEssentialFiles() {
		let instance = adb.getAdbInstance();
		if (instance) {
			// check country
			let country = await adb.pull('/sdcard/coffeevending/country/short');
			// check dev
			let devMode = await adb.pull('/sdcard/coffeevending/CURR_TEST');
			// check .bid
			let boxid = await adb.pull('/sdcard/coffeevending/.bid');

			machineInfoStore.set({
				boxId: boxid,
				versions: {
					firmware: '',
					brew: '',
					xmlengine: '',
					netcore: '',
					devbox: ''
				},
				devMode: devMode?.includes('1') ?? false,
				country: country ?? '',
				status: '',
				errors: []
			});

			handleIncomingMessages(
				JSON.stringify({
					type: 'chat',
					payload: `${new Date().toLocaleTimeString()}: ${get(authStore)?.displayName} has connected to ${boxid}`
				})
			);
		} else {
			addNotification('ERROR:Failed to get machine info');
		}
	}

	async function connectAdb() {
		try {
			if (!('usb' in navigator)) {
				throw new Error('WebUSB not supported, try using fallback method or different browser');
			}

			await adb.connnectViaWebUSB();
			let instance = adb.getAdbInstance();

			if (instance) {
				await startFetchRecipeFromMachine();
				await loadEssentialFiles();
			}
		} catch (e: any) {
			addNotification(`ERROR:${e}`);
		}
	}

	async function tryAutoConnect() {
		try {
			if (!('usb' in navigator) || !AdbDaemonWebUsbDeviceManager.BROWSER) {
				throw new Error('WebUSB not supported, try using fallback method or different browser');
			}

			const devices = await AdbDaemonWebUsbDeviceManager.BROWSER.getDevices();
			if (!devices || devices.length == 0) {
				throw new Error('No device found');
			}

			if (devices.length > 1) {
				throw new Error('Too many connected devices');
			}

			const device = devices[0];
			const credStore = new AdbWebCredentialStore();

			try {
				await adb.connectDeviceByCred(device, credStore);
				return true;
			} catch (e: any) {
				if (e.message === 'CREDENTIAL_EXPIRED') {
					try {
						await deviceCredentialManager.clearAllCredentials();
					} catch (ignored) {}
				}
				if (e instanceof AdbDaemonWebUsbDevice.DeviceBusyError) {
					addNotification(
						'ERR:Device is already in use by another program, please close the program and try again'
					);
				}

				return false;
			}
		} catch (e) {
			console.error('error on auto connect brew page', e);
			addNotification('ERROR:Failed to auto connect, please try again');
		}
	}

	afterNavigate(async () => {
		console.log('after navigate brew');
		await startFetchRecipeFromMachine();
	});

	function getMenuStatus(ms: number): RecipeOverview['status'] {
		switch (ms) {
			case 0:
				return 'ready';
			case 2:
				return 'obsolete';
			case 11:
				return 'pending/online';
			case 12:
				return 'pending/offline';
			default:
				return 'drafted';
		}
	}

	function getMenuCategory(pd: string): string {
		// [country_code]-[category_code]-[drink_Type]-[id]
		let pd_spl = pd.split('-');
		let category = pd_spl[1] ?? '';

		if (category) {
			if (category.endsWith('1')) {
				let result = 'coffee';
				if (category === '01') {
					result += ',v1';
				} else {
					result += ',v2+';
				}

				return result;
			} else if (category.endsWith('2')) {
				return 'tea';
			} else if (category.endsWith('3')) {
				return 'milk';
			} else if (category.endsWith('4')) {
				return 'whey';
			} else if (category.endsWith('5')) {
				return 'soda';
			} else if (category == '99') {
				return 'special';
			}
		}
		return 'unknown';
	}

	function getDrinkType(pd: string): string {
		// [country_code]-[category_code]-[drink_Type]-[id]
		let pd_spl = pd.split('-');
		let drink_type = pd_spl[2] ?? '';

		if (drink_type) {
			if (drink_type.endsWith('1')) {
				return 'hot';
			} else if (drink_type.endsWith('2')) {
				return 'cold / iced';
			} else if (drink_type.endsWith('3')) {
				return 'smoothie / frappe';
			}
		}

		return '';
	}

	// set material used in recipe to tags for using in filter
	function getMainMaterialOfRecipe(rp: any): string {
		let recipeList = rp['recipes'] ?? [];
		let mat_lists = '';

		for (let rpl of recipeList) {
			let mat_id = rpl['materialPathId'];
			let mat_in_use = rpl['isUse'];

			if (mat_in_use && !mat_lists.includes(mat_id)) {
				mat_lists += mat_id + ',';
			}
		}

		mat_lists = mat_lists.substring(0, mat_lists.length - 1);

		return mat_lists;
	}

	function buildTags(rp: any): string {
		let result = '';

		result += getMenuCategory(rp['productCode']);

		let dt = getDrinkType(rp['productCode']);
		let mats = getMainMaterialOfRecipe(rp);

		if (dt !== '') result += ',' + dt;
		if (mats !== '') result += ',' + mats;

		return result;
	}

	function buildOverviewForBrewing() {
		if (devRecipe) {
			let recipe01_query: any = {};
			recipeFromMachine.set(devRecipe);
			data.recipes = [];
			for (let rp of devRecipe['Recipe01']) {
				data.recipes.push({
					productCode: rp['productCode'] ?? '<not set>',
					name: rp['name'] ? rp['name'] : (rp['otherName'] ?? '<not set>'),
					description: rp['desciption']
						? rp['desciption']
						: (rp['otherDescription'] ?? '<not set>'),
					tags: buildTags(rp),
					status: getMenuStatus(rp['MenuStatus'])
				});

				recipe01_query[rp['productCode']] = rp;

				if (rp['SubMenu'] && rp['SubMenu'].length > 0) {
					for (let rps of rp['SubMenu']) {
						data.recipes.push({
							productCode: rps['productCode'] ?? '<not set>',
							name: rps['name'] ? rps['name'] : (rps['otherName'] ?? '<not set>'),
							description: rps['desciption']
								? rps['desciption']
								: (rps['otherDescription'] ?? '<not set>'),
							tags: buildTags(rps),
							status: getMenuStatus(rps['MenuStatus'])
						});

						recipe01_query[rps['productCode']] = rps;
					}
				}
			}

			let materialFromMachine = devRecipe['MaterialSetting'];

			let currentQuery = get(recipeFromMachineQuery);
			currentQuery = {
				...currentQuery,
				recipe: recipe01_query
			};

			let currentMaterialsQuery = materialFromMachine;
			currentQuery = {
				...currentQuery,
				material: currentMaterialsQuery
			};

			recipeFromMachineQuery.set(currentQuery);
			materialFromMachineQuery.set(currentMaterialsQuery);

			toppingListFromMachineQuery.set(devRecipe['Topping']['ToppingList']);
			toppingGroupFromMachineQuery.set(devRecipe['Topping']['ToppingGroup']);
		}
	}

	$effect(() => {
		const brewAppStatusInterval = setInterval(async () => {
			// schedule status from .brew_web_status.log
			let inst = adb.getAdbInstance();
			if (inst && devRecipe) {
				await adb.executeCmd(
					'tail -n 1 /sdcard/coffeevending/.brew_web_status.log > /sdcard/coffeevending/.brew_web_status.latest.log'
				);

				let brew_status_log = await adb.pull(env.PUBLIC_BREW_WEB_LATEST_STATUS);
				if (brew_status_log) {
					let latest_log = brew_status_log;

					if (brew_status !== latest_log) {
						brew_status = latest_log;

						// noti from machine
						if (latest_log.includes('!!!')) {
							let log = latest_log.split('!!!');
							let noti_cfg = log[1];

							if (noti_cfg.includes('=')) {
								let noti_level = noti_cfg.split('=')[1];

								let spl = log[0].split(':');
								let pure_msg = spl[spl.length - 1];

								// case special message
								if (pure_msg.includes('starting retry process')) {
									// is waiting/idle process
									pure_msg = 'Wait for brewing';
								}

								addNotification(`${noti_level}:${pure_msg}`);
							}
						}
					}
				}
			} else if (inst && !devRecipe) {
				// try again
				// await startFetchRecipeFromMachine();
			}
		}, 1000);
		return () => {
			clearInterval(brewAppStatusInterval);
		};
	});
</script>

<div class="mx-8 flex">
	<div class="w-full">
		<div class="mb-4 flex items-center justify-between">
			<div>
				<h1 class="m-8 text-4xl font-bold">Brew</h1>
				<p class="mx-8 my-0 text-muted-foreground">Brewing directly from web to machine</p>
				<p class="mx-8 my-0 text-muted-foreground">
					Note: refreshing page may cut connection with machine
				</p>
			</div>
			<div class="mx-8 my-4 flex gap-2">
				{#if !adb.getAdbInstance() || !devRecipe}
					<Button variant="default" onclick={() => connectAdb()}>Connect</Button>
				{:else}
					<Button variant="default">+ Create Menu</Button>
				{/if}
			</div>

			<!-- <DashboardQuickAdb enableComponent={true} /> -->
		</div>

		<!-- search bar -->

		{#key refresh_counter}
			<div class="w-full">
				{#if $recipeFromMachineLoading}
					<div class="flex items-center justify-center">
						<p class="mx-4">Please wait</p>
						<Spinner />
					</div>
				{:else}
					<DataTable data={data.recipes} refPage="brew" {columns} />
				{/if}
			</div>
		{/key}
	</div>
</div>
