// 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 cli

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	"ble-temperature-sensor-pp-cli/internal/cliutil"
	"ble-temperature-sensor-pp-cli/internal/device"
	"github.com/spf13/cobra"
)

type rootFlags struct {
	asJSON  bool
	live    bool
	address string
	timeout time.Duration
}

// deviceTransport resolves the transport at run time (after flags parse):
// LiveTransport under --live, the replay transport otherwise. Selection cannot
// happen at command-construction time because persistent flags are unparsed then.
func deviceTransport(flags *rootFlags) device.Transport {
	if flags.live {
		return device.NewLiveTransport(flags.address, flags.timeout)
	}
	return device.NewReplayTransport()
}

// novelCommands is an optional hook for hand-authored commands. It is nil by
// default (no extra commands). To extend this CLI WITHOUT editing generated
// files, add a file in package cli — it is preserved across regeneration — that
// sets this var from an init function:
//
//	func init() {
//		novelCommands = func(root *cobra.Command, flags *rootFlags) {
//			root.AddCommand(newMyCmd(flags))
//		}
//	}
var novelCommands func(root *cobra.Command, flags *rootFlags)

func RootCmd() *cobra.Command {
	var flags rootFlags
	return newRootCmd(&flags)
}

func Execute() error {
	return RootCmd().Execute()
}

func ExitCode(err error) int {
	if err == nil {
		return 0
	}
	return 1
}

func newRootCmd(flags *rootFlags) *cobra.Command {
	rootCmd := &cobra.Command{
		Use:          "ble-temperature-sensor-pp-cli",
		Short:        "Control BLE Temperature Sensor over BLE",
		Long:         "Control BLE Temperature Sensor over BLE using a generated device-native CLI surface.",
		Version:      version,
		SilenceUsage: true,
	}
	rootCmd.SetVersionTemplate("ble-temperature-sensor-pp-cli {{ .Version }}\n")
	rootCmd.PersistentFlags().BoolVar(&flags.asJSON, "json", false, "Output as JSON")
	rootCmd.PersistentFlags().BoolVar(&flags.asJSON, "agent", false, "Output agent-friendly JSON")
	rootCmd.PersistentFlags().BoolVar(&flags.live, "live", false, "Contact the physical device over BLE (needs a binary built with -tags ble_live)")
	rootCmd.PersistentFlags().StringVar(&flags.address, "address", "", "BLE device address (default: auto-discover by service UUID)")
	rootCmd.PersistentFlags().DurationVar(&flags.timeout, "timeout", 20*time.Second, "Per-operation BLE timeout")
	rootCmd.AddCommand(newVersionCmd())
	rootCmd.AddCommand(newCapabilitiesCmd(flags))
	rootCmd.AddCommand(newStatusCmd(flags))
	rootCmd.AddCommand(newDoctorCmd(flags))
	rootCmd.AddCommand(newScanCmd(flags))
	if novelCommands != nil {
		novelCommands(rootCmd, flags)
	}
	return rootCmd
}

func writeJSON(cmd *cobra.Command, value any) error {
	enc := json.NewEncoder(cmd.OutOrStdout())
	enc.SetIndent("", "  ")
	return enc.Encode(value)
}

func newCapabilitiesCmd(flags *rootFlags) *cobra.Command {
	return &cobra.Command{
		Use:         "capabilities",
		Short:       "Show generated BLE capability and safety metadata",
		Annotations: map[string]string{"mcp:read-only": "true"},
		RunE: func(cmd *cobra.Command, args []string) error {
			summary := device.Capabilities()
			if flags.asJSON {
				return writeJSON(cmd, summary)
			}
			fmt.Fprintf(cmd.OutOrStdout(), "%s capabilities\n", summary.Device)
			fmt.Fprintf(cmd.OutOrStdout(), "protocol: %s\n", summary.Protocol)
			fmt.Fprintf(cmd.OutOrStdout(), "session: %s\n", summary.SessionMode)
			for _, field := range summary.Telemetry {
				fmt.Fprintf(cmd.OutOrStdout(), "telemetry: %s via %s store=%v\n", field.Name, field.SourceCharacteristicUUID, field.Store)
			}
			for _, command := range summary.Commands {
				if command.Callable {
					fmt.Fprintf(cmd.OutOrStdout(), "callable command: %s safety=%s characteristic=%s\n", command.Name, command.Safety, command.CharacteristicUUID)
					continue
				}
				fmt.Fprintf(cmd.OutOrStdout(), "withheld command: %s safety=%s characteristic=%s reason=%s\n", command.Name, command.Safety, command.CharacteristicUUID, command.WithheldReason)
			}
			return nil
		},
	}
}

func newStatusCmd(flags *rootFlags) *cobra.Command {
	return &cobra.Command{
		Use:         "status",
		Short:       "Read device status (replay-backed by default; live with --live)",
		Annotations: map[string]string{"mcp:read-only": "true"},
		RunE: func(cmd *cobra.Command, args []string) error {
			snapshot, err := deviceTransport(flags).Status(cmd.Context())
			if err != nil {
				return err
			}
			if flags.asJSON {
				return writeJSON(cmd, snapshot)
			}
			fmt.Fprintf(cmd.OutOrStdout(), "%s status\n", snapshot.Device)
			fmt.Fprintf(cmd.OutOrStdout(), "transport: %s\n", snapshot.Transport)
			fmt.Fprintf(cmd.OutOrStdout(), "session: %s\n", snapshot.SessionMode)
			for _, field := range device.StatusFields {
				value := snapshot.Telemetry[field.Name]
				fmt.Fprintf(cmd.OutOrStdout(), "%s: %v\n", field.Name, value)
			}
			return nil
		},
	}
}

func newScanCmd(flags *rootFlags) *cobra.Command {
	return &cobra.Command{
		Use:   "scan",
		Short: "Discover nearby devices by their BLE service (requires --live)",
		Long:  "Scan for devices that expose this device's BLE service UUID(s). Requires --live and a binary built with -tags ble_live.",
		// Inherently live (no replay equivalent) and non-functional through the
		// MCP server, which execs the default, replay-only build. Hidden from MCP.
		Annotations: map[string]string{"mcp:hidden": "true"},
		RunE: func(cmd *cobra.Command, args []string) error {
			if !flags.live {
				if flags.asJSON {
					return writeJSON(cmd, map[string]any{"live": false, "message": "pass --live to scan"})
				}
				fmt.Fprintln(cmd.OutOrStdout(), "not contacting any device; pass --live to scan")
				return nil
			}
			adverts, err := device.NewLiveTransport(flags.address, flags.timeout).Scan(cmd.Context())
			if err != nil {
				return err
			}
			if flags.asJSON {
				return writeJSON(cmd, map[string]any{"count": len(adverts), "devices": adverts})
			}
			if len(adverts) == 0 {
				fmt.Fprintln(cmd.OutOrStdout(), "no devices found (is the device on and any official app closed?)")
				return nil
			}
			for _, advert := range adverts {
				name := advert.Name
				if name == "" {
					name = "(unnamed)"
				}
				fmt.Fprintf(cmd.OutOrStdout(), "%s  %-16s  rssi %d\n", advert.Address, name, advert.RSSI)
			}
			return nil
		},
	}
}

func newDoctorCmd(flags *rootFlags) *cobra.Command {
	return &cobra.Command{
		Use:         "doctor",
		Short:       "Check BLE readiness, build, and (with --live) device reachability",
		Long:        "Report whether live BLE is compiled in, the active verify/dogfood state, the device's service UUIDs, and — with --live — whether the device is reachable. Safe to run anywhere; never actuates the device.",
		Annotations: map[string]string{"mcp:read-only": "true"},
		RunE: func(cmd *cobra.Command, args []string) error {
			info := map[string]any{
				"live_compiled": device.LiveAvailable(),
				"verify_env":    cliutil.IsVerifyEnv(),
				"dogfood_env":   cliutil.IsDogfoodEnv(),
				"service_uuids": device.ServiceUUIDs,
				"address":       flags.address,
				"transport": map[string]any{
					"write_mode":         "acknowledged",
					"command_spacing_ms": 0,
					"poll_cadence_ms":    0,
					"teardown":           "",
					"single_client":      false,
				},
			}
			// Probe hardware only when explicitly live, the BLE backend is
			// compiled in, and not under verify.
			probe := flags.live && device.LiveAvailable() && !cliutil.IsVerifyEnv()
			info["hardware_probe"] = probe
			if probe {
				adverts, err := device.NewLiveTransport(flags.address, flags.timeout).Scan(cmd.Context())
				if err != nil {
					info["scan_error"] = err.Error()
					info["reachable"] = false
				} else {
					info["found"] = len(adverts)
					info["reachable"] = len(adverts) > 0
					info["devices"] = adverts
				}
			}
			if flags.asJSON {
				return writeJSON(cmd, info)
			}
			fmt.Fprintf(cmd.OutOrStdout(), "live BLE compiled: %v\n", info["live_compiled"])
			fmt.Fprintf(cmd.OutOrStdout(), "verify env: %v\n", info["verify_env"])
			fmt.Fprintf(cmd.OutOrStdout(), "dogfood env: %v\n", info["dogfood_env"])
			fmt.Fprintf(cmd.OutOrStdout(), "service uuids: %v\n", info["service_uuids"])
			if !probe {
				if !device.LiveAvailable() {
					fmt.Fprintln(cmd.OutOrStdout(), "hardware probe: skipped (rebuild with -tags ble_live to enable live BLE)")
				} else {
					fmt.Fprintln(cmd.OutOrStdout(), "hardware probe: skipped (pass --live to probe the device)")
				}
				return nil
			}
			if errStr, ok := info["scan_error"].(string); ok {
				fmt.Fprintf(cmd.OutOrStdout(), "reachable: false (%s)\n", errStr)
				return nil
			}
			fmt.Fprintf(cmd.OutOrStdout(), "reachable: %v (found %v device(s))\n", info["reachable"], info["found"])
			return nil
		},
	}
}

func ExecuteWithContext(ctx context.Context) error {
	cmd := RootCmd()
	cmd.SetContext(ctx)
	return cmd.Execute()
}
