package oci

import "testing"

func TestNextSubnetIPv6CIDR(t *testing.T) {
	tests := []struct {
		name    string
		vcnCidr string
		used    []string
		want    string
		wantErr bool
	}{
		{
			name:    "无占用取首个",
			vcnCidr: "2603:c020:e:de00::/56",
			want:    "2603:c020:e:de00::/64",
		},
		{
			name:    "跳过已用索引",
			vcnCidr: "2603:c020:e:de00::/56",
			used:    []string{"2603:c020:e:de00::/64", "2603:c020:e:de01::/64"},
			want:    "2603:c020:e:de02::/64",
		},
		{
			name:    "其他前缀的占用不影响",
			vcnCidr: "2603:c020:e:de00::/56",
			used:    []string{"2603:c020:f:aa00::/64"},
			want:    "2603:c020:e:de00::/64",
		},
		{
			name:    "非法 VCN 前缀报错",
			vcnCidr: "not-a-cidr",
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := nextSubnetIPv6CIDR(tt.vcnCidr, tt.used)
			if (err != nil) != tt.wantErr {
				t.Fatalf("nextSubnetIPv6CIDR() error = %v, wantErr %v", err, tt.wantErr)
			}
			if !tt.wantErr && got != tt.want {
				t.Errorf("nextSubnetIPv6CIDR() = %q, want %q", got, tt.want)
			}
		})
	}
}

func TestNextSubnetIPv6CIDRExhausted(t *testing.T) {
	used := make([]string, 0, 256)
	base := "2603:c020:e:de00::/56"
	for i := 0; i <= 255; i++ {
		cidr, err := nextSubnetIPv6CIDR(base, used)
		if err != nil {
			t.Fatalf("allocation %d failed: %v", i, err)
		}
		used = append(used, cidr)
	}
	if _, err := nextSubnetIPv6CIDR(base, used); err == nil {
		t.Error("257th allocation: got nil error, want exhausted failure")
	}
}

func TestSameIPv6(t *testing.T) {
	tests := []struct {
		name string
		a, b string
		want bool
	}{
		{name: "完全一致", a: "2603:c020:e:de00::1", b: "2603:c020:e:de00::1", want: true},
		{name: "大小写不敏感", a: "2603:C020:E:DE00::1", b: "2603:c020:e:de00::1", want: true},
		{name: "缩写与全写等价", a: "2603:c020:e:de00:0:0:0:1", b: "2603:c020:e:de00::1", want: true},
		{name: "不同地址", a: "2603:c020:e:de00::1", b: "2603:c020:e:de00::2", want: false},
		{name: "非法输入回退字符串比较", a: "not-an-ip", b: "not-an-ip", want: true},
		{name: "非法输入不相等", a: "not-an-ip", b: "2603:c020:e:de00::1", want: false},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := sameIPv6(tt.a, tt.b); got != tt.want {
				t.Errorf("sameIPv6(%q, %q) = %v, want %v", tt.a, tt.b, got, tt.want)
			}
		})
	}
}

func TestBuildRuleOptions(t *testing.T) {
	port := func(n int) *int { return &n }
	tests := []struct {
		name     string
		rule     SecurityRule
		wantTCP  bool
		wantUDP  bool
		wantICMP bool
	}{
		{name: "TCP 端口", rule: SecurityRule{Protocol: "6", PortMin: port(22)}, wantTCP: true},
		{name: "UDP 端口", rule: SecurityRule{Protocol: "17", PortMin: port(53), PortMax: port(53)}, wantUDP: true},
		{name: "ICMP 类型", rule: SecurityRule{Protocol: "1", IcmpType: port(8)}, wantICMP: true},
		{name: "ICMPv6 类型", rule: SecurityRule{Protocol: "58", IcmpType: port(128)}, wantICMP: true},
		{name: "all 无选项", rule: SecurityRule{Protocol: "all"}},
		{name: "TCP 无端口无选项", rule: SecurityRule{Protocol: "6"}},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			tcp, udp, icmp := buildRuleOptions(tt.rule)
			if (tcp != nil) != tt.wantTCP {
				t.Errorf("tcp options = %v, want present=%v", tcp, tt.wantTCP)
			}
			if (udp != nil) != tt.wantUDP {
				t.Errorf("udp options = %v, want present=%v", udp, tt.wantUDP)
			}
			if (icmp != nil) != tt.wantICMP {
				t.Errorf("icmp options = %v, want present=%v", icmp, tt.wantICMP)
			}
			if tt.wantTCP && tt.rule.PortMax == nil {
				if got, want := *tcp.DestinationPortRange.Max, *tt.rule.PortMin; got != want {
					t.Errorf("port max = %d, want fallback to min %d", got, want)
				}
			}
		})
	}
}
