import usbip from "@src/lib/usbip.ts";
import {
    type CommandExecutionError,
    DeviceDetailed,
    type UsbipUnknownError,
} from "@shared/utils/usbip.ts";
import { none } from "@shared/utils/option.ts";
import { InferSchemaType, z } from "@shared/utils/validator.ts";
import log from "@shared/utils/logger.ts";
import { errAsync, ResultAsync } from "@shared/utils/resultasync.ts";

class Devices {
    private readonly devices: Map<string, Device> = new Map();

    updateDevices(): ResultAsync<
        void,
        CommandExecutionError | UsbipUnknownError
    > {
        return usbip.getDevicesDetailed().mapErr((e) => {
            log.error("Failed to update devices!");
            return e;
        }).map((d) => d.unwrapOr([])).map(
            (devices) => {
                const current = new Set(devices.map((d) => d.busid));
                const old = new Set(this.devices.keys());

                const connected = current.difference(old);
                const disconnected = old.difference(current);

                for (const device of devices) {
                    if (connected.has(device.busid)) {
                        this.devices.set(
                            device.busid,
                            this.deviceFromDetailed(device),
                        );
                    }
                }

                for (const device of disconnected) {
                    this.devices.delete(device);
                }
            },
        );
    }

    deviceFromDetailed(d: DeviceDetailed): Device {
        return {
            busid: d.busid,
            usbid: d.usbid,
            vendor: d.vendor,
            name: d.name,
            displayName: none,
            description: none,
            connectedAt: new Date(),
        };
    }
}

export const deviceSchema = z.obj({
    busid: z.string(),
    usbid: z.option(z.string()),
    vendor: z.option(z.string()),
    name: z.option(z.string()),
    displayName: z.option(z.string()),
    description: z.option(z.string()),
    connectedAt: z.date(),
});

const test = new Devices();

await test.updateDevices();

console.log(test);

import { sleep } from "https://deno.land/x/sleep/mod.ts";

await sleep(5);

await test.updateDevices();

console.log(test);

export type Device = InferSchemaType<typeof deviceSchema>;
