// softether-go is a standalone SoftEther VPN client with built-in DHCP and reconnection.
//
// Usage:
//
//	softether-go -host <server> -user <username> -pass <password> [-hub HUB] [-port 443] [-tap tap0] [-insecure]
package main

import (
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"
	"time"

	"git.sagar.ch/sagar/softether-go/pkg/client"
	"git.sagar.ch/sagar/softether-go/pkg/netcfg"
	"git.sagar.ch/sagar/softether-go/pkg/tap"
)

func main() {
	host := flag.String("host", "", "SoftEther server hostname or IP")
	port := flag.Int("port", 443, "SoftEther server port")
	hub := flag.String("hub", "DEFAULT", "Virtual hub name")
	username := flag.String("user", "", "Authentication username")
	password := flag.String("pass", "", "Authentication password")
	plainPass := flag.Bool("plain-password", false, "Send password as plaintext (AuthType 2)")
	tapName := flag.String("tap", "", "TAP interface name (empty = kernel-assigned)")
	macAddr := flag.String("mac", "", "TAP interface MAC address (e.g. 5E:3B:6F:63:A8:3E)")
	insecure := flag.Bool("insecure", false, "Skip TLS certificate verification")
	noDHCP := flag.Bool("no-dhcp", false, "Disable built-in DHCP client")
	reconnectDelay := flag.Duration("reconnect-delay", 5*time.Second, "Delay between reconnection attempts")
	acceptDefaultGW := flag.Bool("accept-default-gateway", false, "Install DHCP-provided gateway as default route")
	acceptStaticRoutes := flag.Bool("accept-static-routes", false, "Install DHCP classless static routes (option 121/249)")
	acceptDNS := flag.Bool("accept-dns", false, "Set /etc/resolv.conf from DHCP-provided DNS servers")
	policyRouteTable := flag.Int("policy-route-table", 0, "Policy routing table: route replies from VPN IP back through VPN gateway")

	flag.Parse()

	if *host == "" || *username == "" {
		fmt.Fprintf(os.Stderr, "Usage: %s -host <server> -user <username> -pass <password> [-hub HUB] [-port 443] [-tap tap0] [-insecure]\n", os.Args[0])
		os.Exit(1)
	}

	cfg := client.Config{
		Host:               *host,
		Port:               *port,
		Hub:                *hub,
		Username:           *username,
		Password:           *password,
		PlainPassword:      *plainPass,
		InsecureSkipVerify: *insecure,
	}

	serverIP := netcfg.ResolveHost(*host)
	cleanupRoute := netcfg.AddServerRoute(serverIP)
	defer cleanupRoute()

	dev, err := tap.Open(*tapName)
	if err != nil {
		log.Fatalf("tap open: %v", err)
	}
	defer dev.Close()
	log.Printf("tap interface: %s", dev.Name)

	if *macAddr != "" {
		hwAddr, err := net.ParseMAC(*macAddr)
		if err != nil {
			log.Fatalf("invalid mac: %v", err)
		}
		if err := dev.SetMAC(hwAddr); err != nil {
			log.Fatalf("set mac: %v", err)
		}
	}

	if err := dev.SetUp(); err != nil {
		log.Fatalf("tap up: %v", err)
	}

	mac, err := dev.MAC()
	if err != nil {
		log.Fatalf("tap mac: %v", err)
	}
	log.Printf("tap mac: %s", mac)

	sig := make(chan os.Signal, 1)
	signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)

	netOpts := netcfg.Options{
		DHCP:             !*noDHCP,
		AcceptDefaultGW:  *acceptDefaultGW,
		AcceptStaticRoutes: *acceptStaticRoutes,
		AcceptDNS:        *acceptDNS,
		PolicyRouteTable: *policyRouteTable,
	}

	for {
		err := runSession(cfg, dev, mac, netOpts, sig)
		if err == errShutdown {
			log.Println("shutting down")
			return
		}
		log.Printf("session ended: %v", err)
		log.Printf("reconnecting in %v...", *reconnectDelay)

		select {
		case <-sig:
			log.Println("shutting down")
			return
		case <-time.After(*reconnectDelay):
		}
	}
}
