package offsite

import (
	"context"
	"errors"
	"fmt"
	"net"
	"time"

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

// SSHHostKeyScanner captures a box's SSH host-key fingerprint by dialing port 23 and grabbing the host key
// from the handshake — BEFORE any auth (there is no credential; the callback aborts once the key is seen).
// This is the hub-side equivalent of `ssh-keyscan`, using x/crypto/ssh so no ssh binary is needed in the
// container. The captured fingerprint is non-secret (a public host-key identifier).
type SSHHostKeyScanner struct {
	Timeout time.Duration
}

// errCaptured aborts the handshake once we have the host key (we never intended to authenticate).
var errCaptured = errors.New("host key captured")

func (s SSHHostKeyScanner) Fingerprint(ctx context.Context, host string, port int) (string, error) {
	timeout := s.Timeout
	if timeout == 0 {
		timeout = 10 * time.Second
	}
	var fp string
	cfg := &ssh.ClientConfig{
		User:    "felhom-keyscan",
		Auth:    nil, // no auth — we abort in the host-key callback
		Timeout: timeout,
		HostKeyCallback: func(_ string, _ net.Addr, key ssh.PublicKey) error {
			fp = ssh.FingerprintSHA256(key)
			return errCaptured
		},
	}
	d := net.Dialer{Timeout: timeout}
	conn, err := d.DialContext(ctx, "tcp", fmt.Sprintf("%s:%d", host, port))
	if err != nil {
		return "", fmt.Errorf("dial: %w", err)
	}
	defer conn.Close()
	// ssh.NewClientConn runs the handshake; our callback captures the key then returns errCaptured, so this
	// returns an error — but fp is set. Any OTHER error (or no key) is a real failure.
	c, chans, reqs, herr := ssh.NewClientConn(conn, host, cfg)
	if c != nil {
		c.Close()
	}
	_ = chans
	_ = reqs
	if fp != "" {
		return fp, nil
	}
	return "", fmt.Errorf("host-key handshake: %w", herr)
}
