// Package tmux provides an interface and implementation for controlling tmux sessions.
package tmux

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

// Client defines the operations the daemon needs on tmux sessions.
type Client interface {
	// HasSession returns true if the named session exists.
	HasSession(name string) bool
	// CreateSession creates a new detached session with an optional working directory.
	CreateSession(name, workdir string) error
	// KillSession destroys the named session.
	KillSession(name string) error
	// SendKeys sends key strokes to the first window of a session.
	SendKeys(session, keys string) error
	// CapturePaneTail returns the last n lines from the session's active pane.
	CapturePaneTail(session string, lines int) (string, error)
}

// ExecClient implements Client by shelling out to the tmux binary.
type ExecClient struct{}

// NewExecClient returns a ready-to-use ExecClient.
func NewExecClient() *ExecClient {
	return &ExecClient{}
}

// run executes a tmux subcommand and returns combined output.
func run(args ...string) (string, error) {
	cmd := exec.Command("tmux", args...)
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &out
	err := cmd.Run()
	return strings.TrimSpace(out.String()), err
}

// HasSession returns true when `tmux has-session` exits 0.
func (c *ExecClient) HasSession(name string) bool {
	_, err := run("has-session", "-t", name)
	return err == nil
}

// CreateSession creates a new detached tmux session.
// If workdir is non-empty, the session starts in that directory.
func (c *ExecClient) CreateSession(name, workdir string) error {
	args := []string{"new-session", "-d", "-s", name}
	if workdir != "" {
		args = append(args, "-c", workdir)
	}
	out, err := run(args...)
	if err != nil {
		return fmt.Errorf("tmux new-session %q: %w (%s)", name, err, out)
	}
	return nil
}

// KillSession destroys the named session.
func (c *ExecClient) KillSession(name string) error {
	out, err := run("kill-session", "-t", name)
	if err != nil {
		return fmt.Errorf("tmux kill-session %q: %w (%s)", name, err, out)
	}
	return nil
}

// SendKeys sends key strokes to the first pane of the session.
func (c *ExecClient) SendKeys(session, keys string) error {
	out, err := run("send-keys", "-t", session, keys, "Enter")
	if err != nil {
		return fmt.Errorf("tmux send-keys %q: %w (%s)", session, err, out)
	}
	return nil
}

// CapturePaneTail captures the last n lines from the session's active pane.
func (c *ExecClient) CapturePaneTail(session string, lines int) (string, error) {
	out, err := run("capture-pane", "-p", "-t", session,
		"-S", fmt.Sprintf("-%d", lines))
	if err != nil {
		return "", fmt.Errorf("tmux capture-pane %q: %w (%s)", session, err, out)
	}
	return out, nil
}
