package ipfilter

import (
	"testing"
)

func TestFilter_New(t *testing.T) {
	tests := []struct {
		name    string
		list    []string
		wantErr bool
	}{
		{
			name:    "empty list",
			list:    []string{},
			wantErr: false,
		},
		{
			name:    "single IP",
			list:    []string{"192.168.1.1"},
			wantErr: false,
		},
		{
			name:    "single CIDR",
			list:    []string{"10.0.0.0/8"},
			wantErr: false,
		},
		{
			name:    "mixed IPs and CIDRs",
			list:    []string{"192.168.1.1", "10.0.0.0/8", "172.16.0.0/12"},
			wantErr: false,
		},
		{
			name:    "invalid IP",
			list:    []string{"999.999.999.999"},
			wantErr: true,
		},
		{
			name:    "invalid CIDR",
			list:    []string{"10.0.0.0/33"},
			wantErr: true,
		},
		{
			name:    "IPv6 address",
			list:    []string{"2001:db8::1"},
			wantErr: false,
		},
		{
			name:    "IPv6 CIDR",
			list:    []string{"2001:db8::/32"},
			wantErr: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			f, err := New(tt.list)
			if (err != nil) != tt.wantErr {
				t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if err == nil && f == nil {
				t.Error("New() should return non-nil filter on success")
			}
		})
	}
}

func TestFilter_ShouldExclude(t *testing.T) {
	f, err := New([]string{
		"192.168.1.1",
		"10.0.0.0/8",
		"172.16.0.0/12",
		"2001:db8::1",
		"fc00::/7",
	})
	if err != nil {
		t.Fatalf("New() error = %v", err)
	}

	tests := []struct {
		name string
		ip   string
		want bool
	}{
		// Exact IP matches
		{"exact match", "192.168.1.1", true},
		{"exact IPv6 match", "2001:db8::1", true},

		// CIDR matches
		{"CIDR match 10.0.0.1", "10.0.0.1", true},
		{"CIDR match 10.255.255.255", "10.255.255.255", true},
		{"CIDR match 172.16.0.1", "172.16.0.1", true},
		{"CIDR match 172.31.255.255", "172.31.255.255", true},
		{"CIDR IPv6 match", "fc00::1", true},

		// No matches
		{"no match 192.168.2.1", "192.168.2.1", false},
		{"no match 11.0.0.1", "11.0.0.1", false},
		{"no match 172.32.0.1", "172.32.0.1", false},
		{"no match 8.8.8.8", "8.8.8.8", false},

		// Invalid IP
		{"invalid IP", "invalid", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := f.ShouldExclude(tt.ip); got != tt.want {
				t.Errorf("ShouldExclude(%q) = %v, want %v", tt.ip, got, tt.want)
			}
		})
	}
}

func TestFilter_ShouldExclude_NilFilter(t *testing.T) {
	var f *Filter
	if f.ShouldExclude("192.168.1.1") {
		t.Error("ShouldExclude on nil filter should return false")
	}
}

func TestFilter_Count(t *testing.T) {
	f, err := New([]string{
		"192.168.1.1",
		"10.0.0.1",
		"10.0.0.0/8",
		"172.16.0.0/12",
	})
	if err != nil {
		t.Fatalf("New() error = %v", err)
	}

	ips, networks := f.Count()
	if ips != 2 {
		t.Errorf("Count() ips = %d, want 2", ips)
	}
	if networks != 2 {
		t.Errorf("Count() networks = %d, want 2", networks)
	}
}

func TestFilter_EmptyEntries(t *testing.T) {
	f, err := New([]string{"", "192.168.1.1", ""})
	if err != nil {
		t.Fatalf("New() error = %v", err)
	}

	ips, _ := f.Count()
	if ips != 1 {
		t.Errorf("Count() ips = %d, want 1 (empty entries should be skipped)", ips)
	}

	if !f.ShouldExclude("192.168.1.1") {
		t.Error("Should exclude 192.168.1.1")
	}
	if f.ShouldExclude("192.168.1.2") {
		t.Error("Should not exclude 192.168.1.2")
	}
}
