// Package main is mavsttd — maven's stt module process.
//
// Per spec: stt/tts are restart-free, key-free, fail-independent modules —
// separate processes from core, reachable over the worker boundary
// (internal/worker). Core dials mavsttd's unix socket and ships audio bytes
// for transcription; mavsttd ships text back.
//
// With -model <path>: loads a whisper.cpp ggml model (e.g. ggml-small.bin)
// for real transcription. Without -model: serves the stub transcriber
// (deterministic no-model floor) so the loop is exercisable end-to-end
// without weights.
//
// Module topology:
//
//	$ mavsttd -socket /run/user/$UID/maven/stt.sock
//
// The daemon's config points at this socket:
//
//	"stt": { "socket": "/run/user/1000/maven/stt.sock" }
//
// Both processes are same-user on the box ⇒ the 0600 socket floor (same
// unix user) is sufficient today; the wg / mTLS cuts in internal/auth are
// for the NETWORK radius (client↔core), not the local module radius.
package main

import (
	"context"
	"errors"
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"

	"github.com/kami/maven/internal/worker"
)

func main() {
	if err := run(os.Args[1:]); err != nil {
		fmt.Fprintln(os.Stderr, "mavsttd:", err)
		os.Exit(1)
	}
}

func run(args []string) error {
	sock := flag.String("socket", defaultSocket("stt.sock"), "unix socket path")
	model := flag.String("model", "", "path to whisper ggml model file")
	minMs := flag.Int("min-ms", 300, "drop clips shorter than this (silence gate); whisper hallucinates on short/quiet audio")
	silenceRMS := flag.Float64("silence-rms", 0.01, "drop clips with normalized RMS below this (silence gate); tune to your mic floor")
	flag.CommandLine.Parse(args)

	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
	defer stop()

	var t worker.Transcriber
	if *model != "" {
		w, err := newWhisperHandler(*model, *minMs, *silenceRMS)
		if err != nil {
			return fmt.Errorf("whisper: %w", err)
		}
		t = w
		defer func() {
			log.Printf("mavsttd: closing whisper model")
			w.Close()
		}()
		log.Printf("mavsttd: loaded whisper model from %s", *model)
	} else {
		log.Printf("mavsttd: no model specified, using stub handler")
		t = &stubHandler{}
	}

	srv := worker.NewServer(*sock, t)
	if err := srv.Listen(); err != nil {
		return err
	}
	defer srv.Close()
	log.Printf("mavsttd: worker listening on %s", srv.Path())

	errCh := make(chan error, 1)
	go func() { errCh <- srv.Serve() }()
	select {
	case <-ctx.Done():
		log.Printf("mavsttd: shutdown signal received")
		srv.Close()
		return nil
	case err := <-errCh:
		if err != nil && !errors.Is(err, net.ErrClosed) {
			return err
		}
		return nil
	}
}

// stubHandler — worker.Transcriber that delegates to the package Stub. Tiny
// now; the production swap replaces this whole struct with a faster-whisper
// / vosk-backed struct (the same Worker.Transcriber interface).
type stubHandler struct{}

func (h *stubHandler) Transcribe(ctx context.Context, req worker.TranscribeReq) (worker.TranscribeResp, error) {
	// delegate to the same deterministic Stub the daemon could have wired
	// in-process; mavsttd is the "separate process" equivalent.
	_ = req
	// hash for variation; same approach as stt.Stub.
	if len(req.Audio.Bytes) == 0 {
		return worker.TranscribeResp{Text: "maven, что у меня сегодня", Confidence: 1.0}, nil
	}
	// vary phrase by first byte for visibility in logs/tests.
	phrases := []string{
		"maven, отметь что я выпил воды",
		"maven, напомни через 4 часа размяться",
		"maven, restart nginx",
		"maven, что у меня сегодня по календарю",
		"note: staggered cooldown by time of day",
		"slept 6h",
	}
	idx := int(req.Audio.Bytes[0]) % len(phrases)
	return worker.TranscribeResp{Text: phrases[idx], Confidence: 1.0}, nil
}

// defaultSocket returns XDG_RUNTIME_DIR/maven/<name> if set, falling back
// to a homedir-relative path (mirrors config.defaultRuntimeDir).
func defaultSocket(name string) string {
	if x := os.Getenv("XDG_RUNTIME_DIR"); x != "" {
		return x + "/maven/" + name
	}
	home, err := os.UserHomeDir()
	if err != nil || home == "" {
		return name
	}
	return home + "/.local/share/maven/" + name
}
