// Package tests provides test infrastructure for TUI SSH testing
package tests

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"testing"
	"time"

	"golang.org/x/crypto/ssh"
)

// SSHTestServer manages an SSH test server container for TUI testing
type SSHTestServer struct {
	Host       string
	Port       int
	User       string
	PrivateKey []byte
	Signer     ssh.Signer
	Container  string
	t          *testing.T
}

// NewSSHTestServer creates and starts an SSH test server in a Docker container
func NewSSHTestServer(t *testing.T) *SSHTestServer {
	t.Helper()

	_, filename, _, ok := runtime.Caller(0)
	if !ok {
		t.Fatal("failed to resolve caller path")
	}

	// Navigate to repo root
	repoRoot := filepath.Clean(filepath.Join(filepath.Dir(filename), "..", ".."))

	// Ensure SSH keys exist
	keysDir := filepath.Join(repoRoot, "deployments", "test_keys")
	privateKeyPath := filepath.Join(keysDir, "test_key")

	if _, err := os.Stat(privateKeyPath); os.IsNotExist(err) {
		t.Skipf("SSH test keys not found at %s. Run: ./scripts/testing/gen-ssh-test-keys.sh", keysDir)
	}

	// Read private key
	privateKey, err := os.ReadFile(privateKeyPath)
	if err != nil {
		t.Fatalf("failed to read private key: %v", err)
	}

	signer, err := ssh.ParsePrivateKey(privateKey)
	if err != nil {
		t.Fatalf("failed to parse private key: %v", err)
	}

	server := &SSHTestServer{
		Host:       "localhost",
		Port:       2222,
		User:       "test",
		PrivateKey: privateKey,
		Signer:     signer,
		Container:  "ml-ssh-test",
		t:          t,
	}

	// Verify container is running
	if err := server.waitForContainer(); err != nil {
		t.Skipf("SSH test container not available: %v. Run: docker-compose -f deployments/docker-compose.prod.smoke.yml up -d ssh-test-server", err)
	}

	// Verify SSH connectivity
	if err := server.waitForSSH(); err != nil {
		t.Fatalf("SSH server not reachable: %v", err)
	}

	return server
}

// waitForContainer checks if the SSH container is running
func (s *SSHTestServer) waitForContainer() error {
	cmd := exec.Command("docker", "ps", "-q", "-f", fmt.Sprintf("name=%s", s.Container))
	output, err := cmd.Output()
	if err != nil {
		return fmt.Errorf("failed to check container status: %w", err)
	}
	if len(output) == 0 {
		return fmt.Errorf("container %s is not running", s.Container)
	}
	return nil
}

// waitForSSH waits for the SSH server to accept connections
func (s *SSHTestServer) waitForSSH() error {
	config := &ssh.ClientConfig{
		User: s.User,
		Auth: []ssh.AuthMethod{
			ssh.PublicKeys(s.Signer),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(), //nolint:gosec // Test only
		Timeout:         5 * time.Second,
	}

	addr := fmt.Sprintf("%s:%d", s.Host, s.Port)

	// Retry with backoff
	for i := 0; i < 10; i++ {
		client, err := ssh.Dial("tcp", addr, config)
		if err == nil {
			client.Close()
			return nil
		}
		time.Sleep(500 * time.Millisecond)
	}

	return fmt.Errorf("SSH server not available after retries")
}

// NewClient creates a new SSH client connection
func (s *SSHTestServer) NewClient() (*ssh.Client, error) {
	config := &ssh.ClientConfig{
		User: s.User,
		Auth: []ssh.AuthMethod{
			ssh.PublicKeys(s.Signer),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(), //nolint:gosec // Test only
		Timeout:         10 * time.Second,
	}

	addr := fmt.Sprintf("%s:%d", s.Host, s.Port)
	return ssh.Dial("tcp", addr, config)
}

// Stop stops the SSH test server container
func (s *SSHTestServer) Stop() {
	// Container is managed by docker-compose, so we don't stop it here
	// This allows multiple tests to reuse the same container
}

// Cleanup stops and removes the SSH test container
func (s *SSHTestServer) Cleanup() {
	cmd := exec.Command("docker", "stop", s.Container)
	_ = cmd.Run()

	cmd = exec.Command("docker", "rm", s.Container)
	_ = cmd.Run()
}

// Exec runs a command on the SSH server and returns output
func (s *SSHTestServer) Exec(cmd string) (string, error) {
	client, err := s.NewClient()
	if err != nil {
		return "", fmt.Errorf("failed to connect: %w", err)
	}
	defer client.Close()

	session, err := client.NewSession()
	if err != nil {
		return "", fmt.Errorf("failed to create session: %w", err)
	}
	defer session.Close()

	output, err := session.CombinedOutput(cmd)
	if err != nil {
		return string(output), fmt.Errorf("command failed: %w", err)
	}

	return string(output), nil
}

// ExecWithPTY runs a command with PTY allocation for interactive TUI testing
func (s *SSHTestServer) ExecWithPTY(cmd string, term string, width, height int) (*ssh.Session, error) {
	client, err := s.NewClient()
	if err != nil {
		return nil, fmt.Errorf("failed to connect: %w", err)
	}

	session, err := client.NewSession()
	if err != nil {
		client.Close()
		return nil, fmt.Errorf("failed to create session: %w", err)
	}

	// Request PTY
	modes := ssh.TerminalModes{
		ssh.ECHO:          0,
		ssh.TTY_OP_ISPEED: 14400,
		ssh.TTY_OP_OSPEED: 14400,
	}

	if err := session.RequestPty(term, width, height, modes); err != nil {
		session.Close()
		client.Close()
		return nil, fmt.Errorf("failed to request pty: %w", err)
	}

	if err := session.Start(cmd); err != nil {
		session.Close()
		client.Close()
		return nil, fmt.Errorf("failed to start command: %w", err)
	}

	return session, nil
}
