// Package rcon provides pluggable RCON adapters. Games speak different
// dialects — Source RCON (Counter-Strike, Minecraft with Mojang auth),
// telnet line protocol (7 Days to Die), HTTP admin APIs (some community
// implementations), stdin-pipe for headless servers without a network
// surface. The Agent picks an adapter based on the module manifest's
// `rcon.adapter` field.
package rcon

import (
	"context"
	"fmt"
	"io"
	"time"
)

// Client is a connected RCON session. Exec runs one command and returns its
// raw text response. Clients must be safe for concurrent Exec calls — the
// poll loop and a future ad-hoc-command path both need to call through the
// same session.
type Client interface {
	Exec(ctx context.Context, cmd string) (string, error)
	Close() error
}

// DialOptions configures one RCON connection attempt.
type DialOptions struct {
	Host           string
	Port           int
	Password       string
	ConnectTimeout time.Duration
	// ReadIdle is how long to wait with no new bytes before treating an Exec
	// response as complete. Telnet has no packet framing, so this heuristic
	// is needed; Source RCON overrides it to 0 (ignored).
	ReadIdle time.Duration

	// ContainerID is the Docker container name or ID for stdio/exec-based
	// adapters that don't use TCP. Ignored by telnet/source_rcon.
	ContainerID string
	// Stdio is the backend that knows how to attach to a container's
	// stdin/stdout. Provided by the Docker runtime when the adapter is
	// "stdio". Nil for TCP adapters.
	Stdio StdioBackend
}

// StdioBackend attaches to a running container's combined stdin/stdout/stderr
// stream. Needed by the stdio adapter for games whose only admin console is
// stdin (Valheim, Terraria, Barotrauma, Enshrouded, Sons Of The Forest).
//
// Returned ReadWriteCloser writes go to the container's stdin; reads are the
// demux-multiplexed stdout+stderr. Close shuts down the hijacked connection.
type StdioBackend interface {
	AttachStdio(ctx context.Context, containerID string) (io.ReadWriteCloser, error)
}

// Dialer establishes RCON connections of a specific adapter kind.
type Dialer interface {
	Dial(ctx context.Context, opts DialOptions) (Client, error)
	Name() string
}

// DialerFor returns the dialer for the given adapter id, or an error if the
// adapter is unknown. Adapter ids come directly from the module manifest's
// `rcon.adapter` field.
func DialerFor(adapter string) (Dialer, error) {
	switch adapter {
	case "telnet":
		return &TelnetDialer{}, nil
	case "source_rcon":
		return &SourceDialer{}, nil
	case "stdio":
		return &StdioDialer{}, nil
	case "websocket_rcon":
		return &WebSocketDialer{}, nil
	case "docker_exec_rcon":
		return &DockerExecDialer{}, nil
	case "be_rcon":
		return &BattlEyeDialer{}, nil
	default:
		return nil, fmt.Errorf("unknown rcon adapter %q (known: telnet, source_rcon, docker_exec_rcon, stdio, websocket_rcon, be_rcon)", adapter)
	}
}
