// Standalone test for the tsnet proxy — verifies the Go layer works
// without needing the Flutter app or iOS build.
//
// Usage:
//   cd tailscale_kit/test
//   go run tsnet_test.go \
//     -authkey=tskey-auth-xxxxx \
//     -heater-ip=100.x.x.x \
//     -heater-port=5050
//
// This will:
//   1. Join the Tailnet with the given auth key
//   2. Open a local TCP proxy to the heater's Tailscale IP
//   3. Make a raw TCP connection through the proxy to verify connectivity
//   4. Print the result and clean up
package main

import (
	"context"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"time"

	"tailscale.com/tsnet"
)

func main() {
	authKey := flag.String("authkey", "", "Tailscale auth key for the phone/test client")
	heaterIP := flag.String("heater-ip", "", "Heater's Tailscale IP (100.x.x.x)")
	heaterPort := flag.Int("heater-port", 5050, "Heater's gRPC port")
	flag.Parse()

	if *authKey == "" || *heaterIP == "" {
		fmt.Fprintf(os.Stderr, "Usage: go run tsnet_test.go -authkey=tskey-auth-xxx -heater-ip=100.x.x.x\n")
		os.Exit(1)
	}

	// Create temp state dir
	stateDir, err := os.MkdirTemp("", "tsnet-test-*")
	if err != nil {
		log.Fatalf("Failed to create state dir: %v", err)
	}
	defer os.RemoveAll(stateDir)

	// Step 1: Start tsnet
	log.Println("[1/4] Starting tsnet...")
	s := &tsnet.Server{
		Dir:       stateDir,
		Hostname:  "constellation-test-client",
		AuthKey:   *authKey,
		Ephemeral: true, // don't persist for tests
	}

	if err := s.Start(); err != nil {
		log.Fatalf("tsnet start failed: %v", err)
	}
	defer s.Close()

	// Get our Tailscale IP
	lc, err := s.LocalClient()
	if err != nil {
		log.Fatalf("local client: %v", err)
	}
	st, err := lc.Status(context.Background())
	if err != nil {
		log.Fatalf("status: %v", err)
	}
	log.Printf("[1/4] Connected to tailnet. Our IP: %v", st.Self.TailscaleIPs)

	// Step 2: Start local proxy
	log.Println("[2/4] Starting local proxy...")
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		log.Fatalf("listen: %v", err)
	}
	defer ln.Close()

	localPort := ln.Addr().(*net.TCPAddr).Port
	remoteAddr := fmt.Sprintf("%s:%d", *heaterIP, *heaterPort)
	log.Printf("[2/4] Proxy: 127.0.0.1:%d → %s (via Tailscale)", localPort, remoteAddr)

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Accept one connection for the test
	go func() {
		conn, err := ln.Accept()
		if err != nil {
			return
		}
		defer conn.Close()

		remote, err := s.Dial(ctx, "tcp", remoteAddr)
		if err != nil {
			log.Printf("[PROXY] Dial failed: %v", err)
			return
		}
		defer remote.Close()

		done := make(chan struct{}, 2)
		go func() { io.Copy(remote, conn); done <- struct{}{} }()
		go func() { io.Copy(conn, remote); done <- struct{}{} }()
		<-done
	}()

	// Step 3: Connect through the proxy
	log.Println("[3/4] Testing TCP connection through proxy...")
	conn, err := net.DialTimeout("tcp", fmt.Sprintf("127.0.0.1:%d", localPort), 10*time.Second)
	if err != nil {
		log.Fatalf("FAIL — could not connect through proxy: %v", err)
	}
	defer conn.Close()

	// For gRPC/HTTP2, we can just check that the TCP connection succeeds
	// and the remote end responds (gRPC sends a settings frame)
	conn.SetReadDeadline(time.Now().Add(5 * time.Second))
	buf := make([]byte, 64)
	n, err := conn.Read(buf)
	if err != nil && err != io.EOF {
		// Timeout is OK — gRPC server waits for client to send first
		if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
			log.Println("[3/4] TCP connection established (gRPC server awaiting client preface)")
		} else {
			log.Printf("[3/4] Read response: %v (this may be OK for gRPC)", err)
		}
	} else {
		log.Printf("[3/4] Received %d bytes from heater", n)
	}

	// Step 4: Result
	log.Println("[4/4] SUCCESS — Tailscale tunnel is working!")
	log.Printf("")
	log.Printf("  Heater reachable at %s via Tailscale", remoteAddr)
	log.Printf("  Local proxy: 127.0.0.1:%d", localPort)
	log.Printf("")
	log.Printf("  Next: use this in the Flutter app:")
	log.Printf("    await tailscaleKit.start(authKey: '%s');", *authKey)
	log.Printf("    final port = await tailscaleKit.startProxy('%s');", *heaterIP)
	log.Printf("    grpcService.connect('127.0.0.1', port: port);")
}
