// Linux test for tsnet — loads the shared library via CGo and tests the tunnel.
//
// Run from macOS via Docker:
//   docker run --rm --platform linux/amd64 \
//     -v /path/to/flutter-tsnet:/src -w /src/test \
//     golang:latest go run linux_test.go \
//       -authkey=tskey-auth-xxx \
//       -heater-ip=100.x.x.x
package main

/*
#cgo LDFLAGS: -L${SRCDIR}/../linux -ltailscale -lresolv -lpthread
#include <stdlib.h>

extern char* TailscaleStart(char*, char*, char*);
extern char* TailscaleStartProxy(char*, int, int*);
extern char* TailscaleStopProxy();
extern char* TailscaleStop();
extern char* TailscaleStatus(char**);
extern char* TailscaleIP(char**);
extern void TailscaleFreeString(char*);
*/
import "C"
import (
	"flag"
	"fmt"
	"net"
	"os"
	"time"
	"unsafe"
)

func goString(cs *C.char) string {
	if cs == nil {
		return ""
	}
	s := C.GoString(cs)
	C.TailscaleFreeString(cs)
	return s
}

func main() {
	authKey := flag.String("authkey", "", "Tailscale auth key")
	heaterIP := flag.String("heater-ip", "", "Heater's Tailscale IP")
	heaterPort := flag.Int("heater-port", 5050, "Heater's port")
	flag.Parse()

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

	// State dir
	stateDir := "/tmp/tsnet-linux-test"
	os.MkdirAll(stateDir, 0o700)

	// 1. Start
	fmt.Println("[1/4] Starting tsnet...")
	start := time.Now()
	dirC := C.CString(stateDir)
	keyC := C.CString(*authKey)
	hostC := C.CString("tsnet-linux-test")
	err := goString(C.TailscaleStart(dirC, keyC, hostC))
	C.free(unsafe.Pointer(dirC))
	C.free(unsafe.Pointer(keyC))
	C.free(unsafe.Pointer(hostC))
	if err != "" {
		fmt.Printf("[1/4] FAIL: %s\n", err)
		os.Exit(1)
	}
	fmt.Printf("[1/4] Started in %dms\n", time.Since(start).Milliseconds())

	// 2. Get IP (with retry)
	fmt.Println("[2/4] Getting Tailscale IP...")
	var ip string
	for i := 0; i < 30; i++ {
		var errOut *C.char
		ipC := C.TailscaleIP(&errOut)
		e := goString(errOut)
		if e == "" {
			ip = goString(ipC)
			break
		}
		C.TailscaleFreeString(ipC)
		time.Sleep(500 * time.Millisecond)
	}
	fmt.Printf("[2/4] Our IP: %s\n", ip)

	// 3. Start proxy
	fmt.Println("[3/4] Starting proxy...")
	ipC := C.CString(*heaterIP)
	var localPort C.int
	errStr := goString(C.TailscaleStartProxy(ipC, C.int(*heaterPort), &localPort))
	C.free(unsafe.Pointer(ipC))
	if errStr != "" {
		fmt.Printf("[3/4] FAIL: %s\n", errStr)
		os.Exit(1)
	}
	fmt.Printf("[3/4] Proxy: localhost:%d → %s:%d\n", int(localPort), *heaterIP, *heaterPort)

	// 4. Test TCP
	fmt.Println("[4/4] Testing TCP connection...")
	conn, connErr := net.DialTimeout("tcp", fmt.Sprintf("127.0.0.1:%d", int(localPort)), 10*time.Second)
	if connErr != nil {
		fmt.Printf("[4/4] TCP FAILED: %v\n", connErr)
	} else {
		fmt.Println("[4/4] TCP SUCCESS")
		conn.Close()
	}

	// Cleanup
	C.TailscaleStopProxy()
	C.TailscaleStop()

	fmt.Println("\nALL TESTS PASSED")
}
