package audio

import (
	"context"
	"fmt"
	"os"
	"os/exec"
	"sync"
	"syscall"
	"time"

	"github.com/jetkvm/kvm/internal/logging"
	"github.com/rs/zerolog"
)

// AudioInputSupervisor manages the audio input server subprocess
type AudioInputSupervisor struct {
	cmd            *exec.Cmd
	cancel         context.CancelFunc
	mtx            sync.Mutex
	running        bool
	logger         zerolog.Logger
	client         *AudioInputClient
	processMonitor *ProcessMonitor
}

// NewAudioInputSupervisor creates a new audio input supervisor
func NewAudioInputSupervisor() *AudioInputSupervisor {
	return &AudioInputSupervisor{
		logger:         logging.GetDefaultLogger().With().Str("component", "audio-input-supervisor").Logger(),
		client:         NewAudioInputClient(),
		processMonitor: GetProcessMonitor(),
	}
}

// Start starts the audio input server subprocess
func (ais *AudioInputSupervisor) Start() error {
	ais.mtx.Lock()
	defer ais.mtx.Unlock()

	if ais.running {
		return fmt.Errorf("audio input supervisor already running with PID %d", ais.cmd.Process.Pid)
	}

	// Create context for subprocess management
	ctx, cancel := context.WithCancel(context.Background())
	ais.cancel = cancel

	// Get current executable path
	execPath, err := os.Executable()
	if err != nil {
		return fmt.Errorf("failed to get executable path: %w", err)
	}

	// Create command for audio input server subprocess
	cmd := exec.CommandContext(ctx, execPath, "--audio-input-server")
	cmd.Env = append(os.Environ(),
		"JETKVM_AUDIO_INPUT_IPC=true", // Enable IPC mode
	)

	// Set process group to allow clean termination
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Setpgid: true,
	}

	ais.cmd = cmd
	ais.running = true

	// Start the subprocess
	err = cmd.Start()
	if err != nil {
		ais.running = false
		cancel()
		return fmt.Errorf("failed to start audio input server process: %w", err)
	}

	ais.logger.Info().Int("pid", cmd.Process.Pid).Msg("Audio input server subprocess started")

	// Add process to monitoring
	ais.processMonitor.AddProcess(cmd.Process.Pid, "audio-input-server")

	// Monitor the subprocess in a goroutine
	go ais.monitorSubprocess()

	// Connect client to the server
	go ais.connectClient()

	return nil
}

// Stop stops the audio input server subprocess
func (ais *AudioInputSupervisor) Stop() {
	ais.mtx.Lock()
	defer ais.mtx.Unlock()

	if !ais.running {
		return
	}

	ais.running = false

	// Disconnect client first
	if ais.client != nil {
		ais.client.Disconnect()
	}

	// Cancel context to signal subprocess to stop
	if ais.cancel != nil {
		ais.cancel()
	}

	// Try graceful termination first
	if ais.cmd != nil && ais.cmd.Process != nil {
		ais.logger.Info().Int("pid", ais.cmd.Process.Pid).Msg("Stopping audio input server subprocess")

		// Send SIGTERM
		err := ais.cmd.Process.Signal(syscall.SIGTERM)
		if err != nil {
			ais.logger.Warn().Err(err).Msg("Failed to send SIGTERM to audio input server")
		}

		// Wait for graceful shutdown with timeout
		done := make(chan error, 1)
		go func() {
			done <- ais.cmd.Wait()
		}()

		select {
		case <-done:
			ais.logger.Info().Msg("Audio input server subprocess stopped gracefully")
		case <-time.After(GetConfig().InputSupervisorTimeout):
			// Force kill if graceful shutdown failed
			ais.logger.Warn().Msg("Audio input server subprocess did not stop gracefully, force killing")
			err := ais.cmd.Process.Kill()
			if err != nil {
				ais.logger.Error().Err(err).Msg("Failed to kill audio input server subprocess")
			}
		}
	}

	ais.cmd = nil
	ais.cancel = nil
}

// IsRunning returns whether the supervisor is running
func (ais *AudioInputSupervisor) IsRunning() bool {
	ais.mtx.Lock()
	defer ais.mtx.Unlock()
	return ais.running
}

// IsConnected returns whether the client is connected to the audio input server
func (ais *AudioInputSupervisor) IsConnected() bool {
	if !ais.IsRunning() {
		return false
	}
	return ais.client.IsConnected()
}

// GetClient returns the IPC client for sending audio frames
func (ais *AudioInputSupervisor) GetClient() *AudioInputClient {
	return ais.client
}

// GetProcessMetrics returns current process metrics if the process is running
func (ais *AudioInputSupervisor) GetProcessMetrics() *ProcessMetrics {
	ais.mtx.Lock()
	defer ais.mtx.Unlock()

	if ais.cmd == nil || ais.cmd.Process == nil {
		// Return default metrics when no process is running
		return &ProcessMetrics{
			PID:           0,
			CPUPercent:    0.0,
			MemoryRSS:     0,
			MemoryVMS:     0,
			MemoryPercent: 0.0,
			Timestamp:     time.Now(),
			ProcessName:   "audio-input-server",
		}
	}

	pid := ais.cmd.Process.Pid
	metrics := ais.processMonitor.GetCurrentMetrics()
	for _, metric := range metrics {
		if metric.PID == pid {
			return &metric
		}
	}
	// Return default metrics if process not found in monitoring
	return &ProcessMetrics{
		PID:           pid,
		CPUPercent:    0.0,
		MemoryRSS:     0,
		MemoryVMS:     0,
		MemoryPercent: 0.0,
		Timestamp:     time.Now(),
		ProcessName:   "audio-input-server",
	}
}

// monitorSubprocess monitors the subprocess and handles unexpected exits
func (ais *AudioInputSupervisor) monitorSubprocess() {
	if ais.cmd == nil || ais.cmd.Process == nil {
		return
	}

	pid := ais.cmd.Process.Pid
	err := ais.cmd.Wait()

	// Remove process from monitoring
	ais.processMonitor.RemoveProcess(pid)

	ais.mtx.Lock()
	defer ais.mtx.Unlock()

	if ais.running {
		// Unexpected exit
		if err != nil {
			ais.logger.Error().Err(err).Int("pid", pid).Msg("Audio input server subprocess exited unexpectedly")
		} else {
			ais.logger.Warn().Int("pid", pid).Msg("Audio input server subprocess exited unexpectedly")
		}

		// Disconnect client
		if ais.client != nil {
			ais.client.Disconnect()
		}

		// Mark as not running
		ais.running = false
		ais.cmd = nil

		ais.logger.Info().Int("pid", pid).Msg("Audio input server subprocess monitoring stopped")
	}
}

// connectClient attempts to connect the client to the server
func (ais *AudioInputSupervisor) connectClient() {
	// Wait briefly for the server to start (reduced from 500ms)
	time.Sleep(GetConfig().DefaultSleepDuration)

	err := ais.client.Connect()
	if err != nil {
		ais.logger.Error().Err(err).Msg("Failed to connect to audio input server")
		return
	}

	ais.logger.Info().Msg("Connected to audio input server")
}

// SendFrame sends an audio frame to the subprocess (convenience method)
func (ais *AudioInputSupervisor) SendFrame(frame []byte) error {
	if ais.client == nil {
		return fmt.Errorf("client not initialized")
	}

	if !ais.client.IsConnected() {
		return fmt.Errorf("client not connected")
	}

	return ais.client.SendFrame(frame)
}

// SendFrameZeroCopy sends a zero-copy frame to the subprocess
func (ais *AudioInputSupervisor) SendFrameZeroCopy(frame *ZeroCopyAudioFrame) error {
	if ais.client == nil {
		return fmt.Errorf("client not initialized")
	}

	if !ais.client.IsConnected() {
		return fmt.Errorf("client not connected")
	}

	return ais.client.SendFrameZeroCopy(frame)
}

// SendConfig sends a configuration update to the subprocess (convenience method)
func (ais *AudioInputSupervisor) SendConfig(config InputIPCConfig) error {
	if ais.client == nil {
		return fmt.Errorf("client not initialized")
	}

	if !ais.client.IsConnected() {
		return fmt.Errorf("client not connected")
	}

	return ais.client.SendConfig(config)
}
