package audiocap

import (
	"fmt"
	"os/exec"
	"strings"
)

const (
	SinkName      = "cheater_mic"
	SourceName    = "CheaterMic"
	RecordingSink = "recording"
)

type Handle struct {
	// virtmic modules
	sinkModuleID   string
	sourceModuleID string

	// recording modules
	recordingSinkID string
	callLoopbackID  string
	ttsLoopbackID   string
}

func Setup() (*Handle, error) {
	h := &Handle{}

	// 1. virtmic null-sink
	sinkID, err := pactlLoad("module-null-sink",
		"sink_name="+SinkName,
		"sink_properties=device.description=CheaterSink",
	)
	if err != nil {
		return nil, fmt.Errorf("null-sink: %w", err)
	}
	h.sinkModuleID = sinkID

	// 2. virtmic remap-source
	sourceID, err := pactlLoad("module-remap-source",
		"source_name="+SourceName,
		"master="+SinkName+".monitor",
		"source_properties=device.description=CheaterMic",
	)
	if err != nil {
		h.Teardown()
		return nil, fmt.Errorf("remap-source: %w", err)
	}
	h.sourceModuleID = sourceID

	// 3. recording null-sink
	recID, err := pactlLoad("module-null-sink",
		"sink_name="+RecordingSink,
		"sink_properties=device.description=Recording",
	)
	if err != nil {
		h.Teardown()
		return nil, fmt.Errorf("recording sink: %w", err)
	}
	h.recordingSinkID = recID

	// 4. Call-audio loopback: resolve default sink monitor explicitly
	defaultMonitor, err := defaultSinkMonitor()
	if err != nil {
		h.Teardown()
		return nil, fmt.Errorf("resolve default monitor: %w", err)
	}
	callID, err := pactlLoad("module-loopback",
		"source="+defaultMonitor,
		"sink="+RecordingSink,
		"latency_msec=20",
	)
	if err != nil {
		h.Teardown()
		return nil, fmt.Errorf("call loopback: %w", err)
	}
	h.callLoopbackID = callID

	// 5. TTS loopback: cheater_mic monitor → recording
	ttsID, err := pactlLoad("module-loopback",
		"source="+SinkName+".monitor",
		"sink="+RecordingSink,
		"latency_msec=20",
	)
	if err != nil {
		h.Teardown()
		return nil, fmt.Errorf("tts loopback: %w", err)
	}
	h.ttsLoopbackID = ttsID

	return h, nil
}

// Teardown unloads in reverse order. Safe to call even after partial Setup.
func (h *Handle) Teardown() {
	if h.ttsLoopbackID != "" {
		_ = pactlUnload(h.ttsLoopbackID)
	}
	if h.callLoopbackID != "" {
		_ = pactlUnload(h.callLoopbackID)
	}
	if h.recordingSinkID != "" {
		_ = pactlUnload(h.recordingSinkID)
	}
	if h.sourceModuleID != "" {
		_ = pactlUnload(h.sourceModuleID)
	}
	if h.sinkModuleID != "" {
		_ = pactlUnload(h.sinkModuleID)
	}
}

func (h *Handle) SinkForPlayback() string {
	return SinkName
}

// defaultSinkMonitor returns "<default-sink-name>.monitor"
func defaultSinkMonitor() (string, error) {
	out, err := exec.Command("pactl", "get-default-sink").Output()
	if err != nil {
		return "", err
	}
	sink := strings.TrimSpace(string(out))
	if sink == "" {
		return "", fmt.Errorf("empty default sink")
	}
	return sink + ".monitor", nil
}

func pactlLoad(module string, args ...string) (string, error) {
	cmdArgs := append([]string{"load-module", module}, args...)
	out, err := exec.Command("pactl", cmdArgs...).Output()
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(out)), nil
}

func pactlUnload(id string) error {
	return exec.Command("pactl", "unload-module", id).Run()
}
