package audio

import (
    "bytes"
    "context"
    "encoding/binary"
    "fmt"
    "io"
    "log"
    "math"
    "os/exec"
    "regexp"
    "strings"
    "sync"
    "time"
)

type Monitor struct {
    mu           sync.RWMutex
    currentLevel int
    cancel       context.CancelFunc
}

func NewMonitor() *Monitor {
    return &Monitor{}
}

func (m *Monitor) GetLevel() int {
    m.mu.RLock()
    defer m.mu.RUnlock()
    return m.currentLevel
}

func (m *Monitor) Start() error {
    ctx, cancel := context.WithCancel(context.Background())
    m.cancel = cancel

    // Используем гибкий поиск USB устройств
    device := m.findUSBDevice()
    log.Printf("Используем аудиоустройство: %s", device)

    cmd := exec.CommandContext(ctx, "arecord",
        "-D", device,
        "-t", "raw",
        "-c", "1",
        "-r", "16000",
        "-f", "S16_LE")

    stdout, err := cmd.StdoutPipe()
    if err != nil {
        cancel()
        return err
    }

    if err := cmd.Start(); err != nil {
        cancel()
        return err
    }

    log.Printf("Аудио-монитор запущен с устройством: %s", device)

    go func() {
        buffer := make([]byte, 3200)
        for {
            select {
            case <-ctx.Done():
                return
            default:
                _, err := io.ReadFull(stdout, buffer)
                if err != nil {
                    log.Printf("Ошибка чтения аудио: %v", err)
                    time.Sleep(2 * time.Second)
                    continue
                }

                samplesCount := len(buffer) / 2
                samples := make([]int16, samplesCount)
                reader := bytes.NewReader(buffer)
                if err := binary.Read(reader, binary.LittleEndian, &samples); err != nil {
                    continue
                }

                var sum float64
                for _, sample := range samples {
                    val := float64(sample)
                    sum += val * val
                }

                rms := math.Sqrt(sum / float64(samplesCount))
                level := int((rms / 32768.0) * 100 * 4)
                if level > 100 {
                    level = 100
                }

                m.mu.Lock()
                m.currentLevel = level
                m.mu.Unlock()
            }
        }
    }()

    go func() {
        _ = cmd.Wait()
    }()

    return nil
}

func (m *Monitor) findUSBDevice() string {
    cmd := exec.Command("arecord", "-l")
    output, err := cmd.Output()
    if err != nil {
        log.Printf("Не удалось получить список устройств: %v", err)
        return "default"
    }

    lines := strings.Split(string(output), "\n")
    for _, line := range lines {
        // Ищем любое USB аудиоустройство
        lowerLine := strings.ToLower(line)
        if strings.Contains(lowerLine, "usb") &&
           (strings.Contains(lowerLine, "audio") || strings.Contains(lowerLine, "sound")) {

            log.Printf("Найдено USB аудиоустройство: %s", line)

            // Извлекаем номера карты и устройства
            re := regexp.MustCompile(`card (\d+):.*device (\d+):`)
            matches := re.FindStringSubmatch(line)
            if len(matches) == 3 {
                device := fmt.Sprintf("plughw:%s,%s", matches[1], matches[2])
                log.Printf("Используем устройство: %s", device)
                return device
            }
        }
    }

    // Если не нашли USB, пробуем найти любое устройство захвата
    for _, line := range lines {
        if strings.Contains(line, "device") && strings.Contains(line, "Capture") {
            re := regexp.MustCompile(`card (\d+):.*device (\d+):`)
            matches := re.FindStringSubmatch(line)
            if len(matches) == 3 {
                device := fmt.Sprintf("plughw:%s,%s", matches[1], matches[2])
                log.Printf("Используем первое найденное устройство: %s", device)
                return device
            }
        }
    }

    log.Println("Аудиоустройства не найдены, используем default")
    return "default"
}

func (m *Monitor) Stop() {
    if m.cancel != nil {
        m.cancel()
    }
}