// Package config provides shared configuration types.
package config

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"os"
	"strconv"
	"strings"
)

// Version info (set via -ldflags)
var (
	Version   = "0.1.0"
	GitCommit = "unknown"
	BuildTime = "unknown"
	GoVersion = "unknown"
)

const (
	DefaultWSPort   = 27183 // WSS signaling
	DefaultSTUNUDP1 = 27182 // UDP STUN port 1
	DefaultSTUNUDP2 = 27183 // UDP STUN port 2
	DefaultSTUNTCP1 = 27180 // TCP STUN port 1
	DefaultSTUNTCP2 = 27181 // TCP STUN port 2
	DefaultWebPort  = 10088 // Web console
	DefaultAPIPort  = 10008 // REST API

	DefaultMaxRelayLoad = 20
	DefaultRelayPort    = 27185

	HeartbeatInterval = 30 // seconds
	HeartbeatTimeout  = 90 // seconds — 3x missed heartbeats → offline
)

// ServerConfig holds inp2ps configuration.
type ServerConfig struct {
	WSPort   int    `json:"wsPort"`
	STUNUDP1 int    `json:"stunUDP1"`
	STUNUDP2 int    `json:"stunUDP2"`
	STUNTCP1 int    `json:"stunTCP1"`
	STUNTCP2 int    `json:"stunTCP2"`
	WebPort  int    `json:"webPort"`
	APIPort  int    `json:"apiPort"`
	DBPath   string `json:"dbPath"`
	CertFile string `json:"certFile"`
	KeyFile  string `json:"keyFile"`
	LogLevel int    `json:"logLevel"` // 0=debug, 1=info, 2=warn, 3=error
	Token    uint64   `json:"token"`    // master token for auth
	Tokens   []uint64 `json:"tokens"`   // additional tenant tokens
	JWTKey   string   `json:"jwtKey"`   // auto-generated if empty

	AdminUser string `json:"adminUser"`
	AdminPass string `json:"adminPass"`
}

func DefaultServerConfig() ServerConfig {
	return ServerConfig{
		WSPort:    DefaultWSPort,
		STUNUDP1:  DefaultSTUNUDP1,
		STUNUDP2:  DefaultSTUNUDP2,
		STUNTCP1:  DefaultSTUNTCP1,
		STUNTCP2:  DefaultSTUNTCP2,
		WebPort:   DefaultWebPort,
		APIPort:   DefaultAPIPort,
		DBPath:    "inp2ps.db",
		LogLevel:  1,
		AdminUser: "admin",
		AdminPass: "admin123",
	}
}

func (c *ServerConfig) FillFromEnv() {
	if v := os.Getenv("INP2PS_WS_PORT"); v != "" {
		c.WSPort, _ = strconv.Atoi(v)
	}
	if v := os.Getenv("INP2PS_WEB_PORT"); v != "" {
		c.WebPort, _ = strconv.Atoi(v)
	}
	if v := os.Getenv("INP2PS_DB_PATH"); v != "" {
		c.DBPath = v
	}
	if v := os.Getenv("INP2PS_TOKEN"); v != "" {
		c.Token, _ = strconv.ParseUint(v, 10, 64)
	}
	if v := os.Getenv("INP2PS_TOKENS"); v != "" {
		parts := strings.Split(v, ",")
		for _, p := range parts {
			p = strings.TrimSpace(p)
			if p == "" {
				continue
			}
			if tv, err := strconv.ParseUint(p, 10, 64); err == nil {
				c.Tokens = append(c.Tokens, tv)
			}
		}
	}
	if v := os.Getenv("INP2PS_CERT"); v != "" {
		c.CertFile = v
	}
	if v := os.Getenv("INP2PS_KEY"); v != "" {
		c.KeyFile = v
	}
	if c.JWTKey == "" {
		b := make([]byte, 32)
		rand.Read(b)
		c.JWTKey = hex.EncodeToString(b)
	}
}

func (c *ServerConfig) Validate() error {
	if c.Token == 0 && len(c.Tokens) == 0 {
		return fmt.Errorf("token is required (INP2PS_TOKEN or INP2PS_TOKENS)")
	}
	return nil
}

// ClientConfig holds inp2pc configuration.
type ClientConfig struct {
	ServerHost string `json:"serverHost"`
	ServerPort int    `json:"serverPort"`
	Node       string `json:"node"`
	Token      uint64 `json:"token"`
	NodeSecret string `json:"nodeSecret,omitempty"`
	User       string `json:"user,omitempty"`
	Insecure   bool   `json:"insecure"` // skip TLS verify

	// STUN ports (defaults match server defaults)
	STUNUDP1 int `json:"stunUDP1,omitempty"`
	STUNUDP2 int `json:"stunUDP2,omitempty"`
	STUNTCP1 int `json:"stunTCP1,omitempty"`
	STUNTCP2 int `json:"stunTCP2,omitempty"`

	RelayEnabled bool `json:"relayEnabled"` // --relay
	SuperRelay   bool `json:"superRelay"`   // --super
	RelayPort    int  `json:"relayPort"`
	MaxRelayLoad int  `json:"maxRelayLoad"`

	ShareBandwidth int `json:"shareBandwidth"` // Mbps
	LogLevel       int `json:"logLevel"`

	Apps []AppConfig `json:"apps"`
}

type AppConfig struct {
	AppName  string `json:"appName"`
	Protocol string `json:"protocol"` // tcp, udp
	SrcPort  int    `json:"srcPort"`
	PeerNode string `json:"peerNode"`
	DstHost  string `json:"dstHost"`
	DstPort  int    `json:"dstPort"`
	Enabled  bool   `json:"enabled"`
}

func DefaultClientConfig() ClientConfig {
	return ClientConfig{
		ServerPort:     DefaultWSPort,
		STUNUDP1:       DefaultSTUNUDP1,
		STUNUDP2:       DefaultSTUNUDP2,
		STUNTCP1:       DefaultSTUNTCP1,
		STUNTCP2:       DefaultSTUNTCP2,
		ShareBandwidth: 10,
		RelayPort:      DefaultRelayPort,
		MaxRelayLoad:   DefaultMaxRelayLoad,
		LogLevel:       1,
	}
}

func (c *ClientConfig) Validate() error {
	if c.ServerHost == "" {
		return fmt.Errorf("serverHost is required")
	}
	if c.Token == 0 && c.NodeSecret == "" {
		return fmt.Errorf("token or nodeSecret is required")
	}
	if c.Node == "" {
		hostname, _ := os.Hostname()
		c.Node = hostname
	}
	return nil
}
