package main

import (
	"log"
	"os"
	"strconv"
	"time"

	"github.com/nixc/reverse-ssh-traefik/internal/client"
)

func envRequired(key string) string {
	v := os.Getenv(key)
	if v == "" {
		log.Fatalf("Required environment variable %s is not set", key)
	}
	return v
}

func envOr(key, fallback string) string {
	if v := os.Getenv(key); v != "" {
		return v
	}
	return fallback
}

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	log.Println("tunnel-client starting")

	serverAddr := envRequired("TUNNEL_SERVER")
	domain := envRequired("TUNNEL_DOMAIN")
	keyPath := envOr("TUNNEL_KEY", "/keys/id_ed25519")

	// Optional HTTP Basic Auth credentials for Traefik middleware.
	authUser := envOr("TUNNEL_AUTH_USER", "")
	authPass := envOr("TUNNEL_AUTH_PASS", "")

	localHost := envOr("TUNNEL_HOST", "127.0.0.1")
	localPortStr := envOr("TUNNEL_PORT", "8080")
	localPort, err := strconv.Atoi(localPortStr)
	if err != nil {
		log.Fatalf("Invalid TUNNEL_PORT=%q: %v", localPortStr, err)
	}

	// Load the private key.
	signer, err := client.LoadPrivateKey(keyPath)
	if err != nil {
		log.Fatalf("Failed to load private key: %v", err)
	}
	log.Printf("Loaded key from %s", keyPath)

	// Reconnect loop.
	backoff := time.Second
	maxBackoff := 30 * time.Second

	for {
		if authUser != "" {
			log.Printf("Connecting to %s (domain=%s, local=%s:%d, basicauth=enabled)", serverAddr, domain, localHost, localPort)
		} else {
			log.Printf("Connecting to %s (domain=%s, local=%s:%d)", serverAddr, domain, localHost, localPort)
		}

		sshClient, err := client.Connect(serverAddr, signer)
		if err != nil {
			log.Printf("Connection failed: %v (retry in %s)", err, backoff)
			time.Sleep(backoff)
			backoff = min(backoff*2, maxBackoff)
			continue
		}

		// Reset backoff on successful connection.
		backoff = time.Second
		log.Printf("Connected to %s", serverAddr)

		// Set up the reverse tunnel (blocks until disconnected).
		if err := client.SetupTunnel(sshClient, domain, localHost, localPort, authUser, authPass); err != nil {
			log.Printf("Tunnel error: %v (reconnecting in %s)", err, backoff)
		}

		sshClient.Close()
		time.Sleep(backoff)
		backoff = min(backoff*2, maxBackoff)
	}
}
