package main

import (
	"crypto/tls"
	"crypto/x509"
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"
)

// Default operator listener
var defaultServerAddr = "0.0.0.0:8080"

// Default agent listeners' addresses
var defaultListenSSLaddr = []string{"192.168.1.4"}
var defaultListenSSLport = "8443"

var defaultListenHTTPSaddr = []string{"192.168.1.4"}
var defaultListenHTTPSport = "8880"

var defaultListenDNSaddr = []string{"192.168.1.4"}
var defaultListenDNSport = "8553"

var defaultListenTCPaddr = []string{"192.168.1.4"}
var defaultListenTCPport = "8888"

// To store server's password
var serverPass *string
var serverHashedPass string

// Developer mode
var DevelMode bool

// Certificates
var serverCertPath = "certificates/server.crt"
var serverKeyPath = "certificates/server.key"
var caCertPath = "certificates/ca.crt"

// Global, as will be shared
var tlsConfig *tls.Config

func main() {

	// log.SetFlags(log.Ltime | log.Lshortfile)
	log.SetFlags(log.Ltime)

	// Handle signals for graceful shutdown
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, os.Interrupt, syscall.SIGTERM)

	// Load avaialbe modules from directory
	InitModules()

	go func() {
		<-stop
		log.Println("Shutting down server gracefully...")
		os.Exit(0)
	}()

	// Parse arguments
	serverPass = flag.String("p", "", "Password for operator authentication")
	serverAddr := flag.String("ip", defaultServerAddr, "IP address and port to listen for operators")
	flag.BoolVar(&DevelMode, "devel", false, "Developer mode")
	flag.Parse()

	// Check if pass is provided
	if *serverPass == "" {
		log.Println("Usage: server.exe -p <password>")
		return
	}
	log.Println("Server password set.")

	// Store hashed server password
	serverHashedPass = HashPassword(serverPass)

	// Load CA certificate
	caCert, err := os.ReadFile(caCertPath)
	if err != nil {
		log.Fatalf("Failed to read CA certificate: %v", err)
	}

	// Load our CA cert for mTLS
	caCertPool := x509.NewCertPool()
	if !caCertPool.AppendCertsFromPEM(caCert) {
		log.Fatal("Failed to append CA certificate to pool")
	}

	// Load certificates
	cert, err := tls.LoadX509KeyPair(serverCertPath, serverKeyPath)
	if err != nil {
		fmt.Println("Error loading certificates:", err)
		return
	}

	// Init TLS context
	tlsConfig = &tls.Config{
		Certificates: []tls.Certificate{cert},
		ClientCAs:    caCertPool,
		ClientAuth:   tls.RequireAndVerifyClientCert, // Client cert will be requested and verified
	}
	// Skip cert verifications if developing or debugging
	if DevelMode {
		log.Print("Skipping mTLS in devel mode")
		tlsConfig.ClientAuth = tls.NoClientCert
	}

	// Goroutines
	// Start default operator listener
	go ListenForOperator(tlsConfig, serverAddr)

	// Start default agent listeners
	if DevelMode {
		StartAgentListener("tcp", defaultListenTCPaddr, defaultListenTCPport, "default_TCP", nil)
		StartAgentListener("ssl", defaultListenSSLaddr, defaultListenSSLport, "default_SSL", nil)
		StartAgentListener("https", defaultListenHTTPSaddr, defaultListenHTTPSport, "default_HTTPS", nil)
		StartAgentListener("dns", defaultListenDNSaddr, defaultListenDNSport, "default_DNS", nil)
	}

	select {}
}

// Start listening for operators
func ListenForOperator(tlsConfig *tls.Config, serverAddr *string) {

	// Start listening for operators
	listener, err := tls.Listen("tcp", *serverAddr, tlsConfig)
	if err != nil {
		log.Fatalf("Error starting operator server: %v", err)
		return
	}
	defer listener.Close()

	// Accept connection via go routine
	for {
		operatorConn, err := listener.Accept()
		if err != nil {
			debugLog("Error accepting connection: %v", err)
			continue
		}
		go HandleOperatorConnection(operatorConn)
	}
}

// Log operator's connection, authenticate and start parsing commands
func HandleOperatorConnection(operatorConn net.Conn) {
	defer func() {
		debugLog("Operator connection closed from: %s", operatorConn.RemoteAddr())
		debugLog("\n")
		operatorConn.Close()
	}()

	// Authenticate operator
	operatorID, authenticated := AuthenticateOperator(operatorConn, serverHashedPass)

	if !authenticated {
		debugLog("Authentication failed")
		return
	}

	// Update dynamic tab completion right after operator connects
	UpdateListenersTabCompletion()
	UpdateAgentsTabCompletion()

	// Handle operator commands after successful authentication
	ParseOperatorCommands(operatorConn, operatorID)
}
