package adb

import (
	"bytes"
	"context"
	"fmt"
	"os/exec"
	"strings"
	"time"
)

type Client struct {
	path string
}

func New(adbPath string) *Client {
	return &Client{path: adbPath}
}

// run executes an adb command and returns stdout.
func (c *Client) run(ctx context.Context, args ...string) (string, error) {
	ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
	defer cancel()

	cmd := exec.CommandContext(ctx, c.path, args...)
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	if err := cmd.Run(); err != nil {
		msg := strings.TrimSpace(stderr.String())
		if msg == "" {
			msg = err.Error()
		}
		return "", fmt.Errorf("adb %s: %s", strings.Join(args, " "), msg)
	}
	return strings.TrimSpace(stdout.String()), nil
}

// Devices returns raw output of `adb devices -l`.
func (c *Client) Devices(ctx context.Context) (string, error) {
	return c.run(ctx, "devices", "-l")
}

// Shell runs a shell command on a specific device and returns stdout.
func (c *Client) Shell(ctx context.Context, deviceID, cmd string) (string, error) {
	return c.run(ctx, "-s", deviceID, "shell", cmd)
}

// Connect connects to a device over TCP/IP (wireless ADB).
func (c *Client) Connect(ctx context.Context, address string) (string, error) {
	return c.run(ctx, "connect", address)
}

// Disconnect disconnects a device.
func (c *Client) Disconnect(ctx context.Context, deviceID string) (string, error) {
	return c.run(ctx, "disconnect", deviceID)
}

// Pull copies a file from the device to a local path.
func (c *Client) Pull(ctx context.Context, deviceID, remotePath, localPath string) error {
	_, err := c.run(ctx, "-s", deviceID, "pull", remotePath, localPath)
	return err
}

// Push copies a local file to the device.
func (c *Client) Push(ctx context.Context, deviceID, localPath, remotePath string) error {
	_, err := c.run(ctx, "-s", deviceID, "push", localPath, remotePath)
	return err
}

// StartServer starts the ADB server if not already running.
func (c *Client) StartServer(ctx context.Context) error {
	_, err := c.run(ctx, "start-server")
	return err
}

// Path returns the resolved ADB binary path.
func (c *Client) Path() string {
	return c.path
}
