package main

import (
	"context"
	"errors"
	"runtime"
	"strings"
	"time"
)

const (
	bluetoothCacheTTL   = 30 * time.Second
	bluetoothctlTimeout = 1500 * time.Millisecond
)

func (c *Collector) collectBluetooth(now time.Time) []BluetoothDevice {
	if len(c.lastBT) > 0 && !c.lastBTAt.IsZero() && now.Sub(c.lastBTAt) < bluetoothCacheTTL {
		return c.lastBT
	}

	if devs, err := readSystemProfilerBluetooth(); err == nil && len(devs) > 0 {
		c.lastBTAt = now
		c.lastBT = devs
		return devs
	}

	if devs, err := readBluetoothCTLDevices(); err == nil && len(devs) > 0 {
		c.lastBTAt = now
		c.lastBT = devs
		return devs
	}

	c.lastBTAt = now
	if len(c.lastBT) == 0 {
		c.lastBT = []BluetoothDevice{{Name: "No Bluetooth info", Connected: false}}
	}
	return c.lastBT
}

func readSystemProfilerBluetooth() ([]BluetoothDevice, error) {
	if runtime.GOOS != "darwin" || !commandExists("system_profiler") {
		return nil, errors.New("system_profiler unavailable")
	}

	ctx, cancel := context.WithTimeout(context.Background(), systemProfilerTimeout)
	defer cancel()

	out, err := runCmd(ctx, "system_profiler", "SPBluetoothDataType")
	if err != nil {
		return nil, err
	}
	return parseSPBluetooth(out), nil
}

func readBluetoothCTLDevices() ([]BluetoothDevice, error) {
	if !commandExists("bluetoothctl") {
		return nil, errors.New("bluetoothctl unavailable")
	}

	ctx, cancel := context.WithTimeout(context.Background(), bluetoothctlTimeout)
	defer cancel()

	out, err := runCmd(ctx, "bluetoothctl", "info")
	if err != nil {
		return nil, err
	}
	return parseBluetoothctl(out), nil
}

func parseSPBluetooth(raw string) []BluetoothDevice {
	lines := strings.Split(raw, "\n")
	var devices []BluetoothDevice
	var currentName string
	var connected bool
	var battery string

	for _, line := range lines {
		trim := strings.TrimSpace(line)
		if len(trim) == 0 {
			continue
		}
		if !strings.HasPrefix(line, "    ") && strings.HasSuffix(trim, ":") {
			// Reset at top-level sections
			currentName = ""
			connected = false
			battery = ""
			continue
		}
		if strings.HasPrefix(line, "        ") && strings.HasSuffix(trim, ":") {
			if currentName != "" {
				devices = append(devices, BluetoothDevice{Name: currentName, Connected: connected, Battery: battery})
			}
			currentName = strings.TrimSuffix(trim, ":")
			connected = false
			battery = ""
			continue
		}
		if strings.Contains(trim, "Connected:") {
			connected = strings.Contains(trim, "Yes")
		}
		if strings.Contains(trim, "Battery Level:") {
			battery = strings.TrimSpace(strings.TrimPrefix(trim, "Battery Level:"))
		}
	}
	if currentName != "" {
		devices = append(devices, BluetoothDevice{Name: currentName, Connected: connected, Battery: battery})
	}
	if len(devices) == 0 {
		return []BluetoothDevice{{Name: "No devices", Connected: false}}
	}
	return devices
}

func parseBluetoothctl(raw string) []BluetoothDevice {
	lines := strings.Split(raw, "\n")
	var devices []BluetoothDevice
	current := BluetoothDevice{}
	for _, line := range lines {
		trim := strings.TrimSpace(line)
		if strings.HasPrefix(trim, "Device ") {
			if current.Name != "" {
				devices = append(devices, current)
			}
			current = BluetoothDevice{Name: strings.TrimPrefix(trim, "Device "), Connected: false}
		}
		if strings.HasPrefix(trim, "Name:") {
			current.Name = strings.TrimSpace(strings.TrimPrefix(trim, "Name:"))
		}
		if strings.HasPrefix(trim, "Connected:") {
			current.Connected = strings.Contains(trim, "yes")
		}
	}
	if current.Name != "" {
		devices = append(devices, current)
	}
	if len(devices) == 0 {
		return []BluetoothDevice{{Name: "No devices", Connected: false}}
	}
	return devices
}
