package handlers

import (
	"encoding/json"
	"fmt"
	"math/rand"
	"net/http"

	"alpine-router/config"
	"alpine-router/nat"
	"alpine-router/network"
)

func HandleFirewall(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case http.MethodGet:
		handleFirewallGet(w, r)
	case http.MethodPost:
		handleFirewallSave(w, r)
	default:
		fail(w, http.StatusMethodNotAllowed, "method not allowed")
	}
}

func handleFirewallGet(w http.ResponseWriter, r *http.Request) {
	cfg, err := config.Load()
	if err != nil {
		fail(w, http.StatusInternalServerError, err.Error())
		return
	}

	// Collect interface names for the UI dropdowns.
	names, _ := network.GetInterfaces()

	ok(w, map[string]interface{}{
		"installed":      nat.IsInstalled(),
		"rules":          cfg.Firewall.Rules,
		"vlan_isolation": cfg.Firewall.VLANIsolation,
		"interfaces":     names,
	})
}

func handleFirewallSave(w http.ResponseWriter, r *http.Request) {
	var body struct {
		Rules         []config.FirewallRule `json:"rules"`
		VLANIsolation bool                  `json:"vlan_isolation"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		fail(w, http.StatusBadRequest, "invalid json: "+err.Error())
		return
	}

	// Assign IDs to new rules that lack one.
	for i := range body.Rules {
		if body.Rules[i].ID == "" {
			body.Rules[i].ID = fmt.Sprintf("%x", rand.Int63())
		}
	}

	cfg, err := config.Load()
	if err != nil {
		fail(w, http.StatusInternalServerError, "load config: "+err.Error())
		return
	}

	cfg.Firewall.Rules = body.Rules
	cfg.Firewall.VLANIsolation = body.VLANIsolation

	if err := config.Save(cfg); err != nil {
		fail(w, http.StatusInternalServerError, "save config: "+err.Error())
		return
	}

	ok(w, map[string]string{"message": "saved"})
}

func HandleFirewallApply(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		fail(w, http.StatusMethodNotAllowed, "method not allowed")
		return
	}

	if !nat.IsInstalled() {
		fail(w, http.StatusServiceUnavailable, "nftables (nft) не установлен — выполните: apk add nftables")
		return
	}

	cfg, err := config.Load()
	if err != nil {
		fail(w, http.StatusInternalServerError, "load config: "+err.Error())
		return
	}

	if err := applyAllRules(cfg); err != nil {
		fail(w, http.StatusInternalServerError, "apply: "+err.Error())
		return
	}

	ok(w, map[string]string{"message": "firewall applied"})
}
