<script lang="ts">import { logger } from '$lib/core/utils/logger';

	import { Button, type ButtonVariant } from './ui/button';
	import * as Card from './ui/card/index';
	import Spinner from '$lib/components/ui/spinner/spinner.svelte';
	import Checkbox from '$lib/components/ui/checkbox/checkbox.svelte';
	import Label from '$lib/components/ui/label/label.svelte';

	import { LockIcon, UnlockIcon, Circle, AlertCircleIcon } from '@lucide/svelte/icons';
	import * as adb from '$lib/core/adb/adb';
	import { machineInfoStore } from '$lib/core/stores/machineInfoStore';
	import { toast } from 'svelte-sonner';
	import { handleIncomingMessages } from '$lib/core/handlers/messageHandler';
	import { auth as authStore } from '$lib/core/stores/auth';
	import { get } from 'svelte/store';
	import {
		AdbDaemonWebUsbDevice,
		AdbDaemonWebUsbDeviceManager
	} from '@yume-chan/adb-daemon-webusb';
	import AdbWebCredentialStore from '@yume-chan/adb-credential-web';
	import { onMount } from 'svelte';
	import { deviceCredentialManager } from '$lib/core/adb/deviceCredManager';
	import { file } from 'zod/mini';
	import { addNotification } from '$lib/core/stores/noti';

	let { enableComponent = true } = $props();

	// connection button states
	let connectionButtonText = $state('Connect');
	let connectionButtonDisable = $state(false);
	let connectionButtonVariant: ButtonVariant = $state('default');

	let connectDeviceOk = $state(false);
	let hasStoredDevice = $state(false);

	let openAppBrewWhenConnected = $state(true);
	let hasOpenedBrewOnce = $state(false);

	// progress
	let showLoadProgress = $state(false);
	//
	let recipe: any | undefined = $state(undefined);

	let machineStatus: string = $state('Ok');
	// machineInfoStore.subscribe((mInfo) => {});

	const essentialFiles = ['/sdcard/coffeevending/versions/'];
	async function loadEssentialFiles() {
		showLoadProgress = true;
		machineStatus = 'Loading infos ...';

		let instance = adb.getAdbInstance();
		if (instance) {
			// check country
			let country = await adb.pull('/sdcard/coffeevending/country/short');
			machineStatus = `Found country: ${country}`;
			// check dev
			let devMode = await adb.pull('/sdcard/coffeevending/CURR_TEST');
			if (devMode?.includes('1')) {
				machineStatus = `Dev mode enabled`;
			} else {
				machineStatus = `Dev mode disabled`;
			}
			// check .bid
			let boxid = await adb.pull('/sdcard/coffeevending/.bid');
			if (boxid) {
				machineStatus = `Box id is ${boxid}`;
			} else {
				machineStatus = 'No box id';
			}

			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 {
			machineStatus = 'Instance lost, try disconnect and re-connect again';
			toast.error('Unexpected Error');
		}
		showLoadProgress = false;
	}

	async function openBrewApp() {
		try {
			let instance = adb.getAdbInstance();
			if (instance) {
				try {
					// bypass
					await adb.executeCmd('echo -n hurr > /sdcard/coffeevending/ignore_pass');
				} catch (e) {}

				let result = await adb.executeCmd(
					'am start -n com.forthvending.coffeemain/com.forthvending.coffeemain.MainActivity'
				);
				if (result?.output) {
					toast.success('Open app success!');
					machineStatus = 'open app success, check the screen and put the password';
				} else if (result?.error) {
					// case usb connection cutoff
					if (result.error === 'ExactReadableEndedError') {
						toast.warning('Connection unstable');
						machineStatus = 'app maybe opened, check the screen';
					} else {
						throw new Error(`Exit ${result.exitCode}. ${result.error}`);
					}
				} else {
					throw new Error('Instance not found or error while executing');
				}

				hasOpenedBrewOnce = true;

				try {
					// bypass
					await adb.executeCmd('echo -n hurr > /sdcard/coffeevending/ignore_pass');
				} catch (e) {}

				setTimeout(async () => {
					try {
						// bypass
						await adb.executeCmd('input tap 336 795');
					} catch (e) {}
				}, 3000);
			}
		} catch (e: any) {
			machineStatus = 'Cannot open brew app';
			toast.error('Error while trying to open brew app, please check the screen. ', {
				description: e.toString()
			});
		}
	}

	async function testPushPullFile() {
		try {
			let instance = adb.getAdbInstance();
			if (instance) {
				await adb.push(
					'/sdcard/coffeevending/test.json',
					JSON.stringify({ test: new Date().toLocaleTimeString() })
				);

				let result = await adb.pull('/sdcard/coffeevending/test.json');

				if (result) {
					if (result === '') {
						logger.info('push pull not ok, get empty');
					} else {
						logger.info('push pull ok', result);
					}
				} else {
					logger.info('push pull not ok', result);
				}

				// clean file
				await adb.executeCmd('rm /sdcard/coffeevending/test.json');
			}
		} catch (error) {
			logger.info('test push file failed', error);
		}
	}

	async function connectAdb() {
		connectionButtonText = '...';
		// lock away no spam
		connectionButtonDisable = true;
		connectionButtonVariant = 'outline';

		try {
			if (!AdbDaemonWebUsbDeviceManager.BROWSER) {
				throw new Error('WebUSB not supported, try using fallback or different browser');
			}

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

			if (instance) {
				await loadEssentialFiles();
				if (openAppBrewWhenConnected) await openBrewApp();
			}
		} catch (e: any) {
			if (e.message === 'CREDENTIAL_EXPIRED') {
				try {
					await deviceCredentialManager.clearAllCredentials();
					hasStoredDevice = false;
				} catch (ignored) {}
			}

			logger.info('error on quick adb: ', e);
			toast.error(`Machine Connection Error`, {
				description: e.toString()
			});
			connectionButtonText = 'Retry';
			connectionButtonVariant = 'default';
			connectDeviceOk = false;
		}
		connectionButtonDisable = false;
	}

	async function disconnectAdb() {
		// clean up no password flag
		// return to engine

		await adb.disconnect();
		connectionButtonText = 'Connect';
		connectionButtonVariant = 'default';

		connectDeviceOk = false;

		// handleIncomingMessages(
		// 	JSON.stringify({
		// 		type: 'chat',
		// 		payload: `${new Date().toLocaleTimeString()}: ${get(authStore)?.displayName} has disconnected!`
		// 	})
		// );
	}

	function checkDeviceConnection() {
		try {
			let instance = adb.getAdbInstance();

			if (instance) {
				// ready
				connectionButtonText = 'Disconnect';
				connectionButtonVariant = 'destructive';

				connectDeviceOk = true;
			} else {
				connectionButtonText = 'Connect';
				connectionButtonVariant = 'default';

				connectDeviceOk = false;
			}
		} catch (e: any) {
			logger.info('error on quick adb: ', e);
			toast.error(`Machine Connection Error`, {
				description: e.toString()
			});
			connectionButtonText = 'Retry';
			connectionButtonVariant = 'default';
			connectDeviceOk = false;
		}
		// connectionButtonDisable = false;
	}

	async function checkStoredCredentials() {
		try {
			if (!AdbDaemonWebUsbDeviceManager.BROWSER) {
				hasStoredDevice = false;
				return;
			}

			const devices = await AdbDaemonWebUsbDeviceManager.BROWSER.getDevices();
			if (!devices || devices.length === 0) {
				hasStoredDevice = false;
				return;
			}

			const credentialStore = new AdbWebCredentialStore();
			let hasKeys = false;

			try {
				for await (const key of credentialStore.iterateKeys()) {
					hasKeys = true;
					break;
				}
			} catch (e) {
				logger.info('check stored error', e);
			}

			hasStoredDevice = devices.length > 0 && hasKeys;
		} catch (e) {
			logger.error('check stored error', e);
			hasStoredDevice = false;
		}
	}

	async function tryAutoConnect(): Promise<boolean> {
		try {
			connectionButtonText = '...';
			// lock away no spam
			connectionButtonDisable = true;
			connectionButtonVariant = 'outline';

			if (!AdbDaemonWebUsbDeviceManager.BROWSER) {
				throw new Error('WebUSB not supported, try using fallback 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();
						hasStoredDevice = false;
					} 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: any) {
			logger.info('error on auto connect adb: ', e);
			toast.error(`Machine Connection Error`, {
				description: e.toString()
			});
			connectionButtonText = 'Connect';
			connectionButtonVariant = 'default';
			connectDeviceOk = false;
		}

		connectionButtonDisable = false;
		return false;
	}

	// update every 1s
	setInterval(async function () {
		checkDeviceConnection();
	}, 1000);

	onMount(async () => {
		await checkStoredCredentials();
		if (!connectDeviceOk && !adb.getAdbInstance()) await tryAutoConnect();
	});
</script>

<div class="p-4">
	<Card.Root class="h-full bg-muted/50">
		<Card.Header>
			<Card.Title>Machine Shortcuts</Card.Title>
			<Card.Description>Shortcuts for machine i.e. connect or hotfix</Card.Description>
			{#if enableComponent}
				<Card.Action>
					<Button
						variant={connectionButtonVariant}
						disabled={connectionButtonDisable}
						onclick={connectDeviceOk ? disconnectAdb : connectAdb}>{connectionButtonText}</Button
					>
				</Card.Action>
			{/if}
		</Card.Header>
		<Card.Content>
			{#if enableComponent}
				<!--  -->
				<div class="items-center space-y-8">
					<!-- <div class="flex w-full items-center gap-2">
						<UnlockIcon />
						<span>This feature is enabled </span>
					</div> -->

					<div class="flex w-full items-center justify-between gap-2">
						<p>
							Device: {connectDeviceOk ? 'Online' : 'Offline'}
						</p>
						<Circle
							class=""
							color={connectDeviceOk ? 'green' : 'red'}
							fill={connectDeviceOk ? 'green' : 'red'}
							size={16}
						/>
					</div>
					<div class="flex w-full items-start gap-2">
						<Checkbox
							id="open_brew_now"
							checked={openAppBrewWhenConnected}
							onCheckedChange={() => {
								openAppBrewWhenConnected = !openAppBrewWhenConnected;
							}}
						/>
						<div class="grid gap-2">
							<Label for="open_brew_now">Show brew app when connected</Label>
							<p class="text-sm text-muted-foreground">
								Immediately try to open brew app when first connected
							</p>
						</div>
					</div>

					<!-- open app brew manual -->
					<div class="flex w-full items-center gap-2">
						<Button variant="default" onclick={openBrewApp} disabled={!connectDeviceOk}
							>Open Brew App</Button
						>
						<Button variant="default" disabled={true}>Refresh Infos</Button>

						<!-- test push file -->
						<Button variant="default" onclick={testPushPullFile} disabled={!connectDeviceOk}
							>Test Push</Button
						>
					</div>
				</div>
			{:else}
				<!-- show lock -->
				<div class="flex w-full items-center justify-center gap-2">
					<LockIcon /> <span>This feature is not enabled </span>
				</div>
			{/if}
		</Card.Content>
		<Card.Footer>
			<!-- Display status -->
			{#if connectDeviceOk}
				{#if showLoadProgress}
					<Spinner />
				{/if}
				<p class="mx-4 font-mono text-sm text-muted-foreground">{machineStatus}</p>
			{/if}
		</Card.Footer>
	</Card.Root>
</div>
