// Package poke is the agent-plane immediate-sync SENDER (Direction-2a,
// SPIKE-immediate-sync-transport-2026-07-16). It is the third structural sibling of
// internal/wgsync and internal/tenantsync: the hub holds a forced-command SSH credential to the
// offsite endpoint (ep0) and drives it; the endpoint stays a dumb, runbook-provisioned box.
//
// A poke is a CONTENTLESS "sync now" nudge: the hub SSHes to ep0's `felhom-poke` forced command
// with the target box's WireGuard /32 as the command string (→ SSH_ORIGINAL_COMMAND); the forced
// command validates it and sends ONE empty UDP datagram from wg0 to that /32:51822. The agent's
// poke listener (felhom-agent internal/poke) fires an immediate desired-state cycle. Measured
// ~0.42 s per fresh SSH session in the spike (target ≤2–3 s).
//
// It carries NO payload semantics, NO auth handshake, NO retry: a lost or forged poke costs at
// most one extra debounced tick, and the 15-min report cycle remains the guarantee. The transport
// is SSH with a PINNED host key (the wgsync posture — exact match or refuse, no insecure
// fallback); credential theft bounds the attacker to "make registered boxes tick", and WireGuard
// itself refuses to encrypt to any /32 no registered peer owns (spike P1 EKEYREJECTED).
package poke

import (
	"bytes"
	"context"
	"fmt"
	"log"
	"net"
	"net/netip"
	"strings"
	"time"

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

// wgSubnet confines every poke target to the WG /24 (defence in depth — the ep0 forced command
// validates independently, and the kernel refuses non-peer /32s).
var wgSubnet = netip.MustParsePrefix("10.77.0.0/24")

// Config configures the SSH poke client. All values come from the deployment env / mounted Secret
// (operator infra — never a customer record). Addr + HostKeyLine mirror wgsync/tenantsync; the
// private key is the poke key's OWN (the ep0 authorized_keys line selects the forced command).
type Config struct {
	Addr        string        // "host:22"
	User        string        // "felhom-peersync" (same user; the key selects the forced command)
	PrivateKey  []byte        // PEM private key (from the mounted Secret file)
	HostKeyLine string        // endpoint host pubkey, authorized_keys format (the wgsync pin)
	Timeout     time.Duration // default 10s (a poke must be quick or abandoned)
}

// Client is a pinned-host-key SSH poke sender. Construct with New (parses keys up front).
type Client struct {
	addr    string
	user    string
	signer  ssh.Signer
	hostKey ssh.PublicKey
	timeout time.Duration
	logger  *log.Logger
}

// New builds a Client, failing early on an unparsable key or host-key line.
func New(cfg Config, logger *log.Logger) (*Client, error) {
	if cfg.Addr == "" || cfg.User == "" {
		return nil, fmt.Errorf("poke: Addr and User are required")
	}
	signer, err := ssh.ParsePrivateKey(cfg.PrivateKey)
	if err != nil {
		return nil, fmt.Errorf("poke: parse private key: %w", err)
	}
	hostKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(cfg.HostKeyLine))
	if err != nil {
		return nil, fmt.Errorf("poke: parse host key line: %w", err)
	}
	timeout := cfg.Timeout
	if timeout == 0 {
		timeout = 10 * time.Second
	}
	if logger == nil {
		logger = log.Default()
	}
	return &Client{addr: cfg.Addr, user: cfg.User, signer: signer, hostKey: hostKey, timeout: timeout, logger: logger}, nil
}

// Poke sends one contentless UDP nudge to boxWGIP via the ep0 forced command. boxWGIP is validated
// to be inside the WG /24 before any connection (the ep0 script validates again). The box IP is
// the SSH command string (the forced command reads $SSH_ORIGINAL_COMMAND). Returns an error on any
// failure — but the CALLER treats this fire-and-forget (log, never fail the operator save).
func (c *Client) Poke(ctx context.Context, boxWGIP string) error {
	addr, err := netip.ParseAddr(boxWGIP)
	if err != nil || !wgSubnet.Contains(addr) {
		return fmt.Errorf("poke: refusing non-WG target %q", boxWGIP)
	}
	sshCfg := &ssh.ClientConfig{
		User:              c.user,
		Auth:              []ssh.AuthMethod{ssh.PublicKeys(c.signer)},
		HostKeyCallback:   ssh.FixedHostKey(c.hostKey),
		HostKeyAlgorithms: []string{c.hostKey.Type()}, // pin the algorithm (wgsync S1 finding)
		Timeout:           c.timeout,
	}
	dialer := net.Dialer{Timeout: c.timeout}
	conn, err := dialer.DialContext(ctx, "tcp", c.addr)
	if err != nil {
		return fmt.Errorf("poke: dial %s: %w", c.addr, err)
	}
	if dl, ok := ctx.Deadline(); ok {
		conn.SetDeadline(dl)
	} else {
		conn.SetDeadline(time.Now().Add(c.timeout))
	}
	sconn, chans, reqs, err := ssh.NewClientConn(conn, c.addr, sshCfg)
	if err != nil {
		conn.Close()
		return fmt.Errorf("poke: ssh handshake %s: %w", c.addr, err)
	}
	client := ssh.NewClient(sconn, chans, reqs)
	defer client.Close()
	conn.SetDeadline(time.Time{})
	if dl, ok := ctx.Deadline(); ok {
		conn.SetDeadline(dl)
	}

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

	var stdout, stderr bytes.Buffer
	session.Stdout = &stdout
	session.Stderr = &stderr
	// The forced command IGNORES this string for execution but reads it as $SSH_ORIGINAL_COMMAND —
	// that is how the target box IP crosses to ep0. The command carries only the IP, nothing else.
	if err := session.Run(boxWGIP); err != nil {
		return fmt.Errorf("poke: remote felhom-poke failed: %w (stderr: %s)", err, strings.TrimSpace(stderr.String()))
	}
	c.logger.Printf("[INFO] poke: sync-poke delivered to %s via %s", boxWGIP, c.addr)
	return nil
}
