// 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 (
	"bufio"
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"time"
)

type TelemetrySample struct {
	Device                   string `json:"device"`
	Field                    string `json:"field"`
	ObservedAt               string `json:"observed_at"`
	SourceCharacteristicUUID string `json:"source_characteristic_uuid"`
	SampleCadence            string `json:"sample_cadence,omitempty"`
	Unit                     string `json:"unit,omitempty"`
	Value                    any    `json:"value"`
}
type SessionSummary struct {
	Device             string   `json:"device"`
	State              string   `json:"state"`
	Mode               string   `json:"mode"`
	Transport          string   `json:"transport"`
	RuntimeDir         string   `json:"runtime_dir,omitempty"`
	EndpointKind       string   `json:"endpoint_kind,omitempty"`
	EndpointPath       string   `json:"endpoint_path,omitempty"`
	TokenPresent       bool     `json:"token_present"`
	ObservedAt         string   `json:"observed_at"`
	Reasons            []string `json:"reasons,omitempty"`
	OneShotFallback    bool     `json:"one_shot_fallback"`
	Reconnect          bool     `json:"reconnect"`
	NotificationStream bool     `json:"notification_stream"`
	Detail             string   `json:"detail,omitempty"`
}
type TelemetryStore struct {
	path string
}

func DefaultTelemetryStorePath(cliName string) (string, error) {
	cacheDir, err := os.UserCacheDir()
	if err != nil {
		return "", fmt.Errorf("resolve user cache dir: %w", err)
	}
	return filepath.Join(cacheDir, cliName, "telemetry.jsonl"), nil
}

func OpenTelemetryStore(path string) (*TelemetryStore, error) {
	if path == "" {
		return nil, fmt.Errorf("telemetry store path is required")
	}
	return &TelemetryStore{path: path}, nil
}

func (s *TelemetryStore) Path() string {
	return s.path
}
func (s *TelemetryStore) SessionPath() string {
	return filepath.Join(filepath.Dir(s.path), "session-summaries.jsonl")
}
func (s *TelemetryStore) CaptureStatus(snapshot StatusSnapshot) ([]TelemetrySample, error) {
	observedAt := snapshot.ObservedAt
	if observedAt == "" {
		observedAt = time.Now().UTC().Format(time.RFC3339)
	}
	samples := []TelemetrySample{}
	for _, field := range StatusFields {
		if !field.Store {
			continue
		}
		value := snapshot.Telemetry[field.Name]
		if wrapped, ok := value.(map[string]any); ok {
			if inner, ok := wrapped["value"]; ok {
				value = inner
			}
		}
		samples = append(samples, TelemetrySample{
			Device:                   snapshot.Device,
			Field:                    field.Name,
			ObservedAt:               observedAt,
			SourceCharacteristicUUID: field.SourceCharacteristicUUID,
			SampleCadence:            field.SampleCadence,
			Unit:                     field.Unit,
			Value:                    value,
		})
	}
	if len(samples) == 0 {
		return samples, nil
	}
	if err := os.MkdirAll(filepath.Dir(s.path), 0o700); err != nil {
		return nil, fmt.Errorf("create telemetry store dir: %w", err)
	}
	file, err := os.OpenFile(s.path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o600)
	if err != nil {
		return nil, fmt.Errorf("open telemetry store: %w", err)
	}
	defer file.Close()
	enc := json.NewEncoder(file)
	for _, sample := range samples {
		if err := enc.Encode(sample); err != nil {
			return nil, fmt.Errorf("write telemetry sample: %w", err)
		}
	}
	return samples, nil
}

func (s *TelemetryStore) CaptureSession(status SessionStatus) (SessionSummary, error) {
	summary := SessionSummary{
		Device:             status.Device,
		State:              status.State,
		Mode:               status.Mode,
		Transport:          status.Transport,
		RuntimeDir:         status.RuntimeDir,
		EndpointKind:       status.Endpoint.Kind,
		EndpointPath:       status.Endpoint.Path,
		TokenPresent:       status.TokenPresent,
		ObservedAt:         status.ObservedAt,
		Reasons:            append([]string(nil), status.Reasons...),
		OneShotFallback:    status.OneShotFallback,
		Reconnect:          status.Reconnect,
		NotificationStream: status.NotificationStream,
		Detail:             status.Detail,
	}
	if summary.ObservedAt == "" {
		summary.ObservedAt = time.Now().UTC().Format(time.RFC3339)
	}
	path := s.SessionPath()
	if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
		return SessionSummary{}, fmt.Errorf("create session summary dir: %w", err)
	}
	file, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o600)
	if err != nil {
		return SessionSummary{}, fmt.Errorf("open session summary store: %w", err)
	}
	defer file.Close()
	if err := json.NewEncoder(file).Encode(summary); err != nil {
		return SessionSummary{}, fmt.Errorf("write session summary: %w", err)
	}
	return summary, nil
}

func (s *TelemetryStore) SessionSummaries() ([]SessionSummary, error) {
	file, err := os.Open(s.SessionPath())
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("open session summary store: %w", err)
	}
	defer file.Close()
	var summaries []SessionSummary
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		var summary SessionSummary
		if err := json.Unmarshal(scanner.Bytes(), &summary); err != nil {
			return nil, fmt.Errorf("decode session summary: %w", err)
		}
		summaries = append(summaries, summary)
	}
	if err := scanner.Err(); err != nil {
		return nil, fmt.Errorf("read session summary store: %w", err)
	}
	return summaries, nil
}

func (s *TelemetryStore) Latest() ([]TelemetrySample, error) {
	file, err := os.Open(s.path)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("open telemetry store: %w", err)
	}
	defer file.Close()
	latestByField := map[string]TelemetrySample{}
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		var sample TelemetrySample
		if err := json.Unmarshal(scanner.Bytes(), &sample); err != nil {
			return nil, fmt.Errorf("decode telemetry sample: %w", err)
		}
		latestByField[sample.Field] = sample
	}
	if err := scanner.Err(); err != nil {
		return nil, fmt.Errorf("read telemetry store: %w", err)
	}
	samples := make([]TelemetrySample, 0, len(latestByField))
	for _, sample := range latestByField {
		samples = append(samples, sample)
	}
	sort.Slice(samples, func(i, j int) bool {
		return samples[i].Field < samples[j].Field
	})
	return samples, nil
}
