import { randomUUID } from "node:crypto";
import { isRecord } from "../internal/rowMappers.js";
export class DeviceRepository {
    #db;
    constructor(db) {
        this.#db = db;
    }
    registerDevice(input) {
        const now = new Date().toISOString();
        const id = randomUUID();
        this.#db
            .prepare(`INSERT INTO devices (id, label, workspace_id, registered_at, updated_at)
         VALUES (?, ?, ?, ?, ?)`)
            .run(id, input.label, input.workspaceId, now, now);
        const row = this.#db
            .prepare("SELECT id, label, workspace_id, registered_at, updated_at, device_public_key_spki, paired_issuer_public_key_spki, paired_at FROM devices WHERE id = ?")
            .get(id);
        if (!isRecord(row))
            throw new Error("Failed to register device.");
        return mapDevice(row);
    }
    /**
     * Upserts the device row for a pairing claim, keyed on the browser-supplied
     * device id (unlike registerDevice, which mints its own UUID). This is the
     * row recordPairing later UPDATEs — without it the pairing claim is a silent
     * no-op (GAP 2). An existing row keeps its original registered_at; only the
     * label/updated_at refresh. The device's workspace_id FK is satisfied by a
     * workspace shell the caller ensures beforehand.
     */
    ensureDeviceForPairing(input) {
        const now = new Date().toISOString();
        this.#db
            .prepare(`INSERT INTO devices (id, label, workspace_id, registered_at, updated_at)
         VALUES (?, ?, ?, ?, ?)
         ON CONFLICT(id) DO UPDATE SET label = excluded.label, updated_at = excluded.updated_at`)
            .run(input.deviceId, input.label, input.workspaceId, now, now);
        const row = this.#db
            .prepare("SELECT id, label, workspace_id, registered_at, updated_at, device_public_key_spki, paired_issuer_public_key_spki, paired_at FROM devices WHERE id = ?")
            .get(input.deviceId);
        if (!isRecord(row))
            throw new Error("Failed to ensure device row.");
        return mapDevice(row);
    }
    findDevice(id) {
        const device = this.#db
            .prepare("SELECT id, label, workspace_id, registered_at, updated_at, device_public_key_spki, paired_issuer_public_key_spki, paired_at FROM devices WHERE id = ?")
            .get(id);
        return isRecord(device) ? mapDevice(device) : undefined;
    }
    findDeviceByWorkspace(workspaceId) {
        const device = this.#db
            .prepare("SELECT id, label, workspace_id, registered_at, updated_at, device_public_key_spki, paired_issuer_public_key_spki, paired_at FROM devices WHERE workspace_id = ? ORDER BY registered_at DESC LIMIT 1")
            .get(workspaceId);
        return isRecord(device) ? mapDevice(device) : undefined;
    }
    listDevices() {
        const rows = this.#db
            .prepare("SELECT id, label, workspace_id, registered_at, updated_at, device_public_key_spki, paired_issuer_public_key_spki, paired_at FROM devices ORDER BY registered_at DESC")
            .all();
        return rows.filter(isRecord).map(mapDevice);
    }
    updateDeviceLabel(id, label) {
        const now = new Date().toISOString();
        this.#db
            .prepare("UPDATE devices SET label = ?, updated_at = ? WHERE id = ?")
            .run(label, now, id);
        return this.findDevice(id);
    }
    /**
     * Records a completed Layer 3 pairing: the device's own public key and the
     * trusted issuer public key remote commands will be verified against.
     */
    recordPairing(input) {
        this.#db
            .prepare(`UPDATE devices
         SET device_public_key_spki = ?,
             paired_issuer_public_key_spki = ?,
             paired_at = ?,
             updated_at = ?
         WHERE id = ?`)
            .run(input.devicePublicKeySpki, input.issuerPublicKeySpki, input.pairedAt, input.pairedAt, input.deviceId);
        return this.findDevice(input.deviceId);
    }
}
function mapDevice(row) {
    return {
        id: String(row.id),
        label: String(row.label),
        workspaceId: String(row.workspace_id),
        registeredAt: String(row.registered_at),
        updatedAt: String(row.updated_at),
        devicePublicKeySpki: optionalString(row.device_public_key_spki),
        pairedIssuerPublicKeySpki: optionalString(row.paired_issuer_public_key_spki),
        pairedAt: optionalString(row.paired_at),
    };
}
function optionalString(value) {
    return typeof value === "string" && value.length > 0 ? value : null;
}
