// inp2pc — INP2P P2P Client
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"syscall"

	"github.com/openp2p-cn/inp2p/internal/client"
	"github.com/openp2p-cn/inp2p/pkg/auth"
	"github.com/openp2p-cn/inp2p/pkg/config"
)

func main() {
	cfg := config.DefaultClientConfig()

	flag.StringVar(&cfg.ServerHost, "serverhost", "", "Server hostname or IP (required)")
	flag.IntVar(&cfg.ServerPort, "serverport", cfg.ServerPort, "Server WSS port")
	flag.StringVar(&cfg.Node, "node", "", "Node name (default: hostname)")
	token := flag.Uint64("token", 0, "Authentication token (uint64)")
	user := flag.String("user", "", "Username for token generation")
	pass := flag.String("password", "", "Password for token generation")
	flag.BoolVar(&cfg.Insecure, "insecure", false, "Skip TLS verification")
	flag.BoolVar(&cfg.RelayEnabled, "relay", false, "Enable relay capability")
	flag.BoolVar(&cfg.SuperRelay, "super", false, "Register as super relay node (implies -relay)")
	flag.IntVar(&cfg.RelayPort, "relay-port", cfg.RelayPort, "Relay listen port")
	flag.IntVar(&cfg.MaxRelayLoad, "relay-max", cfg.MaxRelayLoad, "Max concurrent relay sessions")
	flag.IntVar(&cfg.ShareBandwidth, "bw", cfg.ShareBandwidth, "Share bandwidth (Mbps)")
	flag.IntVar(&cfg.STUNUDP1, "stun-udp1", cfg.STUNUDP1, "UDP STUN port 1")
	flag.IntVar(&cfg.STUNUDP2, "stun-udp2", cfg.STUNUDP2, "UDP STUN port 2")
	flag.IntVar(&cfg.STUNTCP1, "stun-tcp1", cfg.STUNTCP1, "TCP STUN port 1")
	flag.IntVar(&cfg.STUNTCP2, "stun-tcp2", cfg.STUNTCP2, "TCP STUN port 2")
	flag.IntVar(&cfg.LogLevel, "log-level", cfg.LogLevel, "Log level")
	configFile := flag.String("config", "config.json", "Config file path")
	newConfig := flag.Bool("newconfig", false, "Ignore existing config, use command line args only")
	version := flag.Bool("version", false, "Print version and exit")
	flag.Parse()

	if *version {
		fmt.Printf("inp2pc version %s\ncommit: %s\nbuild: %s\ngo: %s\n",
			config.Version, config.GitCommit, config.BuildTime, config.GoVersion)
		os.Exit(0)
	}

	// Load config file first (unless -newconfig)
	if !*newConfig {
		if data, err := os.ReadFile(*configFile); err == nil {
			var fileCfg config.ClientConfig
			if err := json.Unmarshal(data, &fileCfg); err == nil {
				cfg = fileCfg
				// fill defaults for missing fields
				if cfg.ServerPort == 0 {
					cfg.ServerPort = config.DefaultWSPort
				}
				if cfg.STUNUDP1 == 0 {
					cfg.STUNUDP1 = config.DefaultSTUNUDP1
				}
				if cfg.STUNUDP2 == 0 {
					cfg.STUNUDP2 = config.DefaultSTUNUDP2
				}
				if cfg.STUNTCP1 == 0 {
					cfg.STUNTCP1 = config.DefaultSTUNTCP1
				}
				if cfg.STUNTCP2 == 0 {
					cfg.STUNTCP2 = config.DefaultSTUNTCP2
				}
				if cfg.RelayPort == 0 {
					cfg.RelayPort = config.DefaultRelayPort
				}
				if cfg.MaxRelayLoad == 0 {
					cfg.MaxRelayLoad = config.DefaultMaxRelayLoad
				}
				if cfg.ShareBandwidth == 0 {
					cfg.ShareBandwidth = 10
				}
				if cfg.LogLevel == 0 {
					cfg.LogLevel = 1
				}
				log.Printf("[main] loaded config from %s", *configFile)
			}
		}
	}

	// Command line flags override config file
	flag.Visit(func(f *flag.Flag) {
		switch f.Name {
		case "serverhost":
			cfg.ServerHost = f.Value.String()
		case "serverport":
			fmt.Sscanf(f.Value.String(), "%d", &cfg.ServerPort)
		case "node":
			cfg.Node = f.Value.String()
		case "insecure":
			cfg.Insecure = true
		case "relay":
			cfg.RelayEnabled = true
		case "super":
			cfg.SuperRelay = true
			cfg.RelayEnabled = true // super implies relay
		case "bw":
			fmt.Sscanf(f.Value.String(), "%d", &cfg.ShareBandwidth)
		}
	})

	// Token from flag or credentials
	if *token > 0 {
		cfg.Token = *token
	} else if *user != "" && *pass != "" {
		cfg.Token = auth.MakeToken(*user, *pass)
		log.Printf("[main] token: %d", cfg.Token)
	}

	if err := cfg.Validate(); err != nil {
		log.Fatalf("[main] config error: %v", err)
	}

	log.Printf("[main] inp2pc v%s starting", config.Version)
	log.Printf("[main] node=%s server=%s:%d relay=%v super=%v",
		cfg.Node, cfg.ServerHost, cfg.ServerPort, cfg.RelayEnabled, cfg.SuperRelay)

	// Save config
	if data, err := json.MarshalIndent(cfg, "", "  "); err == nil {
		os.WriteFile(*configFile, data, 0644)
	}

	// Create and run client
	c := client.New(cfg)

	// Handle shutdown
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		<-sigCh
		log.Println("[main] shutting down...")
		c.Stop()
	}()

	if err := c.Run(); err != nil {
		log.Fatalf("[main] client error: %v", err)
	}

	log.Println("[main] goodbye")
}
