package device

import (
	"context"
	"strings"

	"git.achmad.dev/admin/droidscope/backend/adb"
)

type Manager struct {
	adb    *adb.Client
	Client *adb.Client // exported for use by other packages
}

func NewManager(client *adb.Client) *Manager {
	return &Manager{adb: client, Client: client}
}

// Info fetches static device information for the given device ID.
func (m *Manager) Info(ctx context.Context, deviceID string) (*DeviceInfo, error) {
	return FetchInfo(ctx, m.adb, deviceID)
}

// LiveStats fetches dynamic device stats for the given device ID.
func (m *Manager) LiveStats(ctx context.Context, deviceID string) (*DeviceLiveStats, error) {
	return FetchLiveStats(ctx, m.adb, deviceID)
}

// List returns all currently connected devices with their details.
func (m *Manager) List(ctx context.Context) ([]Device, error) {
	raw, err := m.adb.Devices(ctx)
	if err != nil {
		return nil, err
	}
	serials := parseDeviceLines(raw)
	devices := make([]Device, 0, len(serials))
	for serial, status := range serials {
		d := Device{
			ID:             serial,
			Status:         status,
			ConnectionType: connectionType(serial),
		}
		if status == StatusOnline {
			enrichDevice(ctx, m.adb, &d)
		}
		devices = append(devices, d)
	}
	return devices, nil
}

// Connect connects to a wireless ADB device by address (host:port).
func (m *Manager) Connect(ctx context.Context, address string) error {
	out, err := m.adb.Connect(ctx, address)
	if err != nil {
		return err
	}
	if strings.Contains(out, "failed") || strings.Contains(out, "error") {
		return &Error{Message: out}
	}
	return nil
}

// Disconnect disconnects a device.
func (m *Manager) Disconnect(ctx context.Context, deviceID string) error {
	_, err := m.adb.Disconnect(ctx, deviceID)
	return err
}

// parseDeviceLines parses `adb devices -l` output into a serial→status map.
func parseDeviceLines(raw string) map[string]Status {
	result := make(map[string]Status)
	for _, line := range strings.Split(raw, "\n") {
		line = strings.TrimSpace(line)
		if line == "" || strings.HasPrefix(line, "List of") {
			continue
		}
		fields := strings.Fields(line)
		if len(fields) < 2 {
			continue
		}
		serial := fields[0]
		result[serial] = parseStatus(fields[1])
	}
	return result
}

func parseStatus(s string) Status {
	switch s {
	case "device":
		return StatusOnline
	case "offline":
		return StatusOffline
	case "unauthorized":
		return StatusUnauthorized
	case "recovery":
		return StatusRecovery
	default:
		return StatusOffline
	}
}

func connectionType(serial string) ConnectionType {
	if strings.Contains(serial, ":") {
		return ConnectionWifi
	}
	return ConnectionUSB
}

// enrichDevice fetches device properties via adb shell getprop.
func enrichDevice(ctx context.Context, client *adb.Client, d *Device) {
	props := map[string]*string{
		"ro.product.model":        &d.Model,
		"ro.product.manufacturer": &d.Manufacturer,
		"ro.build.version.release": &d.AndroidVersion,
		"ro.product.cpu.abi":      &d.ABI,
		"ro.product.name":         &d.Product,
	}
	for prop, dest := range props {
		if val, err := client.Shell(ctx, d.ID, "getprop "+prop); err == nil {
			*dest = strings.TrimSpace(val)
		}
	}
	if level, err := client.Shell(ctx, d.ID, "dumpsys battery | grep level | tr -dc '0-9'"); err == nil {
		d.BatteryLevel = parseIntSafe(strings.TrimSpace(level))
	}
}

func parseIntSafe(s string) int {
	n := 0
	for _, c := range s {
		if c >= '0' && c <= '9' {
			n = n*10 + int(c-'0')
		}
	}
	return n
}

type Error struct {
	Message string
}

func (e *Error) Error() string { return e.Message }
