<script lang="ts">
  import { onDestroy, type Snippet } from 'svelte';
  import { get } from 'svelte/store';
  import { serialState, setSerialContext } from './serial.store';
  import * as service from './serial.service';
  import { getDeviceName } from '$lib/utils/device';

  interface Props {
    children?: Snippet;
  }

  let { children }: Props = $props();

  async function requestPort() {
    serialState.update((state) => ({
      ...state,
      status: 'requesting',
      error: undefined,
    }));

    try {
      const port = await service.requestPort();
      if (!port) return null;

      const info = port.getInfo?.();
      const name = info
        ? (getDeviceName(info) ??
          `USB ${info.usbVendorId ?? ''}:${info.usbProductId ?? ''}`)
        : 'Serial Device';
      serialState.update((s) => ({
        ...s,
        port,
        portName: name,
        status: 'idle',
      }));

      return port;
    } catch (err) {
      serialState.update((s) => ({
        ...s,
        status: 'error',
        error: String(err),
      }));
      return null;
    }
  }

  async function openPort(options: SerialOptions) {
    let port = get(serialState).port;
    if (!port) return;

    serialState.update((s) => ({
      ...s,
      status: 'connecting',
      error: undefined,
    }));

    try {
      await service.openPort(port, options);
      serialState.update((s) => ({ ...s, status: 'connected' }));
    } catch (err) {
      serialState.update((s) => ({
        ...s,
        status: 'error',
        error: String(err),
      }));
    }
  }

  async function closePort() {
    let port = get(serialState).port;

    if (!port) return;

    serialState.update((s) => ({ ...s, status: 'disconnecting' }));
    await service.closePort(port);

    serialState.update((s) => ({
      ...s,
      status: 'idle',
    }));
  }

  async function reopenPort(options: SerialOptions) {
    await closePort();
    await openPort(options);
  }

  setSerialContext({
    state: {
      subscribe: serialState.subscribe,
    },
    requestPort,
    openPort,
    reopenPort,
    closePort,
  });

  onDestroy(() => {
    let port = get(serialState).port;
    if (port) service.closePort(port);
  });
</script>

{@render children?.()}
