package main

import (
	"context"
	"os"
	"os/signal"
	"syscall"
	"time"

	"firewall_containers/network-go/config"
	"firewall_containers/network-go/docker"
	"firewall_containers/network-go/firewall"
	"firewall_containers/network-go/iptables"
	"firewall_containers/network-go/logger"
	"firewall_containers/network-go/watcher"
)

// Config path - can be overridden via environment variable
const defaultConfigPath = "/etc/user/config/networks.json"

// Log path - can be overridden via environment variable
const defaultLogPath = "/var/log/network-go/network-go.log"

// Watch period - can be overridden via environment variable
const defaultWatchPeriod = 30 * time.Second

func getEnv(key, defaultVal string) string {
	if val := os.Getenv(key); val != "" {
		return val
	}
	return defaultVal
}

func getWatchPeriod() time.Duration {
	periodStr := os.Getenv("WATCH_PERIOD_SECONDS")
	if periodStr == "" {
		return defaultWatchPeriod
	}
	seconds, err := time.ParseDuration(periodStr + "s")
	if err != nil {
		logger.Warn("MAIN: invalid WATCH_PERIOD_SECONDS=%s, using default %s", periodStr, defaultWatchPeriod)
		return defaultWatchPeriod
	}
	return seconds
}

func getDebug() bool {
	return os.Getenv("DEBUG") == "1" || os.Getenv("DEBUG") == "true"
}

func main() {
	configPath := getEnv("NETWORKS_CONFIG_PATH", defaultConfigPath)
	logPath := getEnv("NETWORK_GO_LOG_PATH", defaultLogPath)
	watchPeriod := getWatchPeriod()
	debug := getDebug()

	// Initialize the dual-output logger (stdout + file)
	log := logger.NewLogger(debug, logPath)
	logger.SetDefault(log)

	log.Info("MAIN: starting network-go firewall container manager")
	log.Info("MAIN: config path = %s", configPath)
	log.Info("MAIN: log path = %s", logPath)
	log.Info("MAIN: watch period = %s", watchPeriod)
	log.Info("MAIN: debug = %v", debug)

	// Create Docker client (uses DOCKER_HOST env var automatically)
	dockerClient, err := docker.NewClient()
	if err != nil {
		log.Error("MAIN: failed to create Docker client: %v", err)
		os.Exit(1)
	}
	defer dockerClient.Close()
	log.Info("MAIN: Docker client created")

	// Create iptables manager
	iptablesMgr := iptables.NewManager(debug)
	log.Info("MAIN: iptables manager created (binary=%s)", iptablesMgr.Binary())

	ctx := context.Background()

	// Load initial config
	cfg, err := config.Load(configPath)
	if err != nil {
		log.Error("MAIN: failed to load initial config: %v", err)
		os.Exit(1)
	}
	log.Info("MAIN: config loaded: %d networks, %d IPs, %d policies",
		len(cfg.Networks), len(cfg.IPs), len(cfg.Policies))

	// Create the firewall orchestrator (needs config for resolver)
	orchestrator := firewall.NewOrchestrator(dockerClient, iptablesMgr, cfg)

	// Run full reconciliation
	orchestrator.ReconcileAll(ctx, cfg)

	// Set up file watcher to detect changes and re-reconcile
	onChange := func() {
		log.Info("MAIN: config file change detected, reloading and reconciling")
		newCfg, err := config.Load(configPath)
		if err != nil {
			log.Error("MAIN: failed to reload config: %v", err)
			return
		}
		log.Info("MAIN: config reloaded: %d networks, %d IPs, %d policies",
			len(newCfg.Networks), len(newCfg.IPs), len(newCfg.Policies))
		orchestrator.ReconcileAll(ctx, newCfg)
	}

	fileWatcher := watcher.NewFileWatcher(configPath, watchPeriod, onChange)
	fileWatcher.Start()

	// Wait for shutdown signal
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)

	sig := <-sigCh
	log.Info("MAIN: received signal %v, shutting down", sig)
	fileWatcher.Stop()
	log.Info("MAIN: shutdown complete")
}