package config

import (
	"encoding/json"
	"fmt"
	"net"
	"os"
)

// NetworksConfig represents the /etc/user/config/networks.json structure
type NetworksConfig struct {
	Networks map[string]NetworkConfig `json:"networks"`
	IPs      map[string]IPConfig      `json:"ips"`
	Policies []PolicyConfig           `json:"policies"`
}

// NetworkConfig represents a single network definition
type NetworkConfig struct {
	NetworkName string `json:"network_name"`
	Subnet      string `json:"subnet"`
	Gateway     string `json:"gateway"`
}

// IPConfig represents a single IP assignment
type IPConfig struct {
	IP            string `json:"ip"`
	ContainerName string `json:"container_name"`
	Selector      string `json:"selector"`
	ServiceName   string `json:"service_name"`
}

// PolicyConfig represents a single policy rule from networks.json
type PolicyConfig struct {
	ServiceName   string `json:"service_name,omitempty"`
	ContainerName string `json:"container_name,omitempty"`
	Selector      string `json:"selector,omitempty"`
	From          string `json:"from,omitempty"`
	To            string `json:"to,omitempty"`
	Port          int    `json:"port,omitempty"`
	Proto         string `json:"proto,omitempty"`
	Name          string `json:"name,omitempty"`
	Iface         string `json:"iface,omitempty"`
	Nat           string `json:"nat,omitempty"`
}

// FirewallRule is a resolved, executable firewall rule derived from a PolicyConfig
type FirewallRule struct {
	// Source info
	SourceIP   string
	SourcePort int
	SourceIface string

	// Target info
	TargetIP   string
	TargetPort int

	// Protocol (tcp/udp)
	Proto string

	// Chain and table
	Chain string // PREROUTING, POSTROUTING, FORWARD, DOCKER-USER, etc.
	Table string // nat, filter

	// Action
	Action string // DNAT, MASQUERADE, ACCEPT, DROP

	// Comment for iptables
	Comment string

	// Namespace info (empty = host, non-empty = container PID namespace)
	ContainerPID string
}

// ToCIDR converts an IP without mask to a /24 CIDR notation (matching shell script behavior)
func ToCIDR(ip string) string {
	// If it's already a CIDR, return as-is
	if _, _, err := net.ParseCIDR(ip); err == nil {
		return ip
	}
	// If last octet is 0, it's already a network address
	parsed := net.ParseIP(ip)
	if parsed == nil {
		return ip
	}
	ipv4 := parsed.To4()
	if ipv4 == nil {
		return ip
	}
	if ipv4[3] == 0 {
		return fmt.Sprintf("%d.%d.%d.0/24", ipv4[0], ipv4[1], ipv4[2])
	}
	return ip
}

// NetworkPrefix returns the first three octets as a /24 CIDR
func NetworkPrefix(ip string) string {
	parsed := net.ParseIP(ip)
	if parsed == nil {
		return ip
	}
	ipv4 := parsed.To4()
	if ipv4 == nil {
		return ip
	}
	return fmt.Sprintf("%d.%d.%d.0/24", ipv4[0], ipv4[1], ipv4[2])
}

// IsIP checks if a string is an IPv4 address
func IsIP(s string) bool {
	parsed := net.ParseIP(s)
	return parsed != nil && parsed.To4() != nil
}

// ParseCIDR parses the subnet string into an IPNet
func (n NetworkConfig) ParseCIDR() (*net.IPNet, error) {
	_, ipNet, err := net.ParseCIDR(n.Subnet)
	if err != nil {
		return nil, fmt.Errorf("failed to parse subnet %s: %w", n.Subnet, err)
	}
	return ipNet, nil
}

// Load reads and parses the networks.json configuration file
func Load(path string) (*NetworksConfig, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("failed to read config file %s: %w", path, err)
	}

	var config NetworksConfig
	if err := json.Unmarshal(data, &config); err != nil {
		return nil, fmt.Errorf("failed to parse config file %s: %w", path, err)
	}

	return &config, nil
}