package config

import (
	"os"
	"path/filepath"
	"testing"
)

const testJSON = `{
  "networks": {
    "smarthost-loadbalancer": {
      "network_name": "smarthost-loadbalancer",
      "subnet": "172.18.103.0/24",
      "gateway": "172.18.103.1"
    },
    "smarthost_backend-1": {
      "network_name": "smarthost_backend-1",
      "subnet": "172.18.104.0/24",
      "gateway": "172.18.104.1"
    }
  },
  "ips": {
    "172.18.103.2": {
      "ip": "172.18.103.2",
      "container_name": "smarthostloadbalancer",
      "selector": "smarthostloadbalancer",
      "service_name": "smarthost-proxy"
    },
    "172.18.104.2": {
      "ip": "172.18.104.2",
      "container_name": "smarthostbackend-1",
      "selector": "smarthostbackend-1",
      "service_name": "smarthost-proxy"
    }
  },
  "policies": [
    {
      "service_name": "smarthost-proxy",
      "container_name": "smarthost_loadbalancer",
      "selector": "smarthostloadbalancer",
      "from": "publicbackend",
      "port": 80,
      "proto": "tcp"
    },
    {
      "service_name": "smarthost-proxy",
      "container_name": "smarthost_loadbalancer",
      "selector": "smarthostloadbalancer",
      "name": "wireguardproxy",
      "iface": "wg0",
      "nat": "dnat",
      "to": "smarthostloadbalancer",
      "port": 80,
      "proto": "tcp"
    }
  ]
}`

func TestLoad(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "networks.json")
	if err := os.WriteFile(path, []byte(testJSON), 0644); err != nil {
		t.Fatalf("failed to write test file: %v", err)
	}

	cfg, err := Load(path)
	if err != nil {
		t.Fatalf("Load() returned error: %v", err)
	}

	if len(cfg.Networks) != 2 {
		t.Errorf("expected 2 networks, got %d", len(cfg.Networks))
	}

	if cfg.Networks["smarthost-loadbalancer"].Subnet != "172.18.103.0/24" {
		t.Errorf("unexpected subnet: %s", cfg.Networks["smarthost-loadbalancer"].Subnet)
	}

	if len(cfg.IPs) != 2 {
		t.Errorf("expected 2 IPs, got %d", len(cfg.IPs))
	}

	if cfg.IPs["172.18.103.2"].ContainerName != "smarthostloadbalancer" {
		t.Errorf("unexpected container name: %s", cfg.IPs["172.18.103.2"].ContainerName)
	}

	if len(cfg.Policies) != 2 {
		t.Errorf("expected 2 policies, got %d", len(cfg.Policies))
	}

	if cfg.Policies[0].From != "publicbackend" {
		t.Errorf("unexpected from: %s", cfg.Policies[0].From)
	}

	if cfg.Policies[1].Nat != "dnat" {
		t.Errorf("unexpected nat: %s", cfg.Policies[1].Nat)
	}
}

func TestLoadFileNotFound(t *testing.T) {
	_, err := Load("/nonexistent/path/networks.json")
	if err == nil {
		t.Error("expected error for nonexistent file, got nil")
	}
}

func TestLoadInvalidJSON(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "bad.json")
	if err := os.WriteFile(path, []byte("{invalid json"), 0644); err != nil {
		t.Fatalf("failed to write test file: %v", err)
	}

	_, err := Load(path)
	if err == nil {
		t.Error("expected error for invalid JSON, got nil")
	}
}

func TestToCIDR(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"172.18.103.0", "172.18.103.0/24"},
		{"172.18.103.2", "172.18.103.2"},
		{"172.18.103.0/24", "172.18.103.0/24"},
		{"10.0.0.0", "10.0.0.0/24"},
		{"invalid", "invalid"},
	}

	for _, tt := range tests {
		got := ToCIDR(tt.input)
		if got != tt.want {
			t.Errorf("ToCIDR(%q) = %q, want %q", tt.input, got, tt.want)
		}
	}
}

func TestNetworkPrefix(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"172.18.103.2", "172.18.103.0/24"},
		{"10.0.0.1", "10.0.0.0/24"},
		{"invalid", "invalid"},
	}

	for _, tt := range tests {
		got := NetworkPrefix(tt.input)
		if got != tt.want {
			t.Errorf("NetworkPrefix(%q) = %q, want %q", tt.input, got, tt.want)
		}
	}
}

func TestIsIP(t *testing.T) {
	tests := []struct {
		input string
		want  bool
	}{
		{"172.18.103.2", true},
		{"10.0.0.0", true},
		{"255.255.255.255", true},
		{"publicbackend", false},
		{"172.18.103.0/24", false},
		{"", false},
	}

	for _, tt := range tests {
		got := IsIP(tt.input)
		if got != tt.want {
			t.Errorf("IsIP(%q) = %v, want %v", tt.input, got, tt.want)
		}
	}
}

func TestNetworkConfigParseCIDR(t *testing.T) {
	nc := NetworkConfig{Subnet: "172.18.103.0/24"}
	ipNet, err := nc.ParseCIDR()
	if err != nil {
		t.Fatalf("ParseCIDR() returned error: %v", err)
	}
	if ipNet.String() != "172.18.103.0/24" {
		t.Errorf("ParseCIDR() = %s, want 172.18.103.0/24", ipNet.String())
	}

	nc2 := NetworkConfig{Subnet: "invalid"}
	_, err = nc2.ParseCIDR()
	if err == nil {
		t.Error("expected error for invalid subnet, got nil")
	}
}

func TestLoadReproducible(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "networks.json")
	if err := os.WriteFile(path, []byte(testJSON), 0644); err != nil {
		t.Fatalf("failed to write test file: %v", err)
	}

	// Load twice and compare
	cfg1, err := Load(path)
	if err != nil {
		t.Fatalf("first Load() error: %v", err)
	}

	cfg2, err := Load(path)
	if err != nil {
		t.Fatalf("second Load() error: %v", err)
	}

	// Verify reproducibility
	if len(cfg1.Networks) != len(cfg2.Networks) {
		t.Errorf("reproducibility: network count mismatch %d vs %d", len(cfg1.Networks), len(cfg2.Networks))
	}
	if len(cfg1.IPs) != len(cfg2.IPs) {
		t.Errorf("reproducibility: IP count mismatch %d vs %d", len(cfg1.IPs), len(cfg2.IPs))
	}
	if len(cfg1.Policies) != len(cfg2.Policies) {
		t.Errorf("reproducibility: policy count mismatch %d vs %d", len(cfg1.Policies), len(cfg2.Policies))
	}

	for name, net1 := range cfg1.Networks {
		net2 := cfg2.Networks[name]
		if net1.Subnet != net2.Subnet || net1.Gateway != net2.Gateway {
			t.Errorf("reproducibility: network %s mismatch", name)
		}
	}
}