// Copyright 2026. Licensed under Apache-2.0. See LICENSE.
// Generated by CLI Printing Press (https://github.com/mvanhorn/cli-printing-press). DO NOT EDIT.

package device

import (
	"context"
	"errors"
)

// Advert is a discovered BLE advertisement.
type Advert struct {
	Address      string   `json:"address"`
	Name         string   `json:"name"`
	RSSI         int      `json:"rssi"`
	ServiceUUIDs []string `json:"service_uuids,omitempty"`
}

// ErrLiveUnavailable is returned by every live operation when the binary was
// built without the BLE backend (the default, pure-Go, no-CGO build).
var ErrLiveUnavailable = errors.New("live BLE is not compiled into this binary; rebuild with: go build -tags ble_live ./... (CGO required)")

// ErrDeviceNotFound is returned when a scan finds no matching device.
var ErrDeviceNotFound = errors.New("no matching device found; power it on and close any official app (only one BLE client can connect at a time)")

// ErrVerifyMode is returned by Dial under PRINTING_PRESS_VERIFY: verify must
// never actuate hardware. Hand-authored commands should gate on
// cliutil.IsVerifyEnv() before dialing; this is the backstop.
var ErrVerifyMode = errors.New("live BLE is disabled under verify mode")

// bleBackend is the device-neutral BLE central surface. The live implementation
// (tinygo bluetooth, build tag ble_live) and the pure-Go stub live in separate
// build-tagged files; both provide newBLEBackend and the liveCompiled constant.
type bleBackend interface {
	Scan(ctx context.Context, serviceUUIDs []string) ([]Advert, error)
	Connect(ctx context.Context, address string, serviceUUIDs []string) (Link, error)
}

// Link is a connected device. Write/Read/Subscribe address GATT characteristics
// by UUID, so one connection serves every command and telemetry stream the
// device spec declares. Hand-authored commands obtain a Link via Dial.
type Link interface {
	Write(characteristicUUID string, payload []byte) error
	Read(characteristicUUID string) ([]byte, error)
	Subscribe(characteristicUUID string, handler func([]byte)) error
	Close() error
}

// LiveAvailable reports whether a live BLE backend is compiled into this binary
// (true only when built with -tags ble_live).
func LiveAvailable() bool { return liveCompiled }

// bleBackendFactory opens the BLE backend. It is a var so tests can inject a
// fake backend and exercise LiveTransport without real hardware; production code
// leaves it pointing at the build-tag-selected newBLEBackend.
var bleBackendFactory = newBLEBackend

// DeviceCodec adapts a device whose protocol cannot be driven from static
// captured evidence (vendor framing, scaling, checksums, parameterized values).
// Implement it in an operator-owned file and register it from an init function
// (codec = myCodec{}). With a codec, the generated command surface gains:
//   - EncodeCommand: build the payload for a command, using its positional CLI
//     args for parameterized commands (e.g. set-speed <kmh>). Return the
//     captured command.PayloadHex unchanged for commands you don't transform.
//   - DecodeTelemetry: turn a raw telemetry frame into a typed value for the
//     generated status command.
//
// The default (nil codec) is Tier-1: write captured payloads, surface raw-hex
// telemetry. Stateful choreography (hold a connection and poll) still belongs in
// hand-authored commands built on Dial + Link.
type DeviceCodec interface {
	EncodeCommand(command CommandDefinition, args []string) ([]byte, error)
	DecodeTelemetry(field StatusField, raw []byte) (any, error)
}

// codec is the optional telemetry decoder. nil by default (Tier-1: raw hex).
var codec DeviceCodec

// telemetrySnapshot, when set, captures one raw telemetry frame from a
// notify-only device: it subscribes, elicits a frame (for example by sending a
// poll command), and returns the first usable notification. Status decodes every
// field from that single frame instead of GATT-reading each characteristic,
// which push-only telemetry does not support (a read returns stale or echoed
// bytes). nil keeps the GATT-read path for readable telemetry. Register it from
// an operator file alongside the codec; the operator owns the subscription, so
// unlike a connect-time hook it runs at the right point in the read flow.
var telemetrySnapshot func(ctx context.Context, link Link) ([]byte, error)
