package utils

import (
	"testing"

	. "github.com/onsi/gomega"
)

func TestValidSecondaryCidr(t *testing.T) {
	g := NewGomegaWithT(t)

	t.Run("valid cases", func(t *testing.T) {
		// Valid private CIDR in same range as primary (10.x)
		valid, err := ValidSecondaryCidr("10.0.0.0/16", []string{}, "10.1.0.0/16")
		g.Expect(valid).To(BeTrue())
		g.Expect(err).To(BeNil())

		// Valid with existing secondary CIDRs
		valid, err = ValidSecondaryCidr("10.0.0.0/16", []string{"10.1.0.0/16"}, "10.2.0.0/16")
		g.Expect(valid).To(BeTrue())
		g.Expect(err).To(BeNil())

		// Valid 172.16 range
		valid, err = ValidSecondaryCidr("172.16.0.0/16", []string{}, "172.17.0.0/16")
		g.Expect(valid).To(BeTrue())
		g.Expect(err).To(BeNil())

		// Valid 192.168 range
		valid, err = ValidSecondaryCidr("192.168.0.0/17", []string{}, "192.168.128.0/24")
		g.Expect(valid).To(BeTrue())
		g.Expect(err).To(BeNil())

		valid, err = ValidSecondaryCidr("172.16.8.0/21", []string{}, "172.16.16.0/21")
		g.Expect(valid).To(BeTrue())
		g.Expect(err).To(BeNil())

		valid, err = ValidSecondaryCidr("172.16.8.0/21", []string{}, "172.30.0.0/16")
		g.Expect(err).To(BeNil())
		g.Expect(valid).To(BeTrue())
	})

	t.Run("invalid primary CIDR", func(t *testing.T) {
		valid, err := ValidSecondaryCidr("invalid-cidr", []string{}, "10.1.0.0/16")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError(ContainSubstring("invalid primary CIDR")))
	})

	t.Run("invalid new secondary CIDR", func(t *testing.T) {
		valid, err := ValidSecondaryCidr("10.0.0.0/16", []string{}, "invalid-cidr")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError(ContainSubstring("invalid new secondary CIDR")))
	})

	t.Run("non-private primary CIDR", func(t *testing.T) {
		valid, err := ValidSecondaryCidr("203.0.113.0/24", []string{}, "10.1.0.0/16")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError("primary CIDR must be a private CIDR"))
	})

	t.Run("non-private secondary CIDR", func(t *testing.T) {
		valid, err := ValidSecondaryCidr("10.0.0.0/16", []string{}, "203.0.113.0/24")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError("secondary CIDR must be a private CIDR"))
	})

	t.Run("IPv6 not supported", func(t *testing.T) {
		// IPv6 primary
		valid, err := ValidSecondaryCidr("2001:db8::/32", []string{}, "10.1.0.0/16")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError("only IPv4 CIDRs are supported"))

		// IPv6 secondary
		valid, err = ValidSecondaryCidr("10.0.0.0/16", []string{}, "2001:db8::/32")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError("only IPv4 CIDRs are supported"))
	})

	t.Run("prefix length restrictions", func(t *testing.T) {
		// Too small prefix (/15)
		valid, err := ValidSecondaryCidr("10.0.0.0/16", []string{}, "10.1.0.0/15")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError("secondary CIDR block must have a prefix length between /16 and /28"))

		// Too large prefix (/29)
		valid, err = ValidSecondaryCidr("10.0.0.0/16", []string{}, "10.1.0.0/29")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError("secondary CIDR block must have a prefix length between /16 and /28"))

		// Valid prefix lengths
		valid, err = ValidSecondaryCidr("10.0.0.0/16", []string{}, "10.1.0.0/16")
		g.Expect(valid).To(BeTrue())
		g.Expect(err).To(BeNil())

		valid, err = ValidSecondaryCidr("10.0.0.0/16", []string{}, "10.1.0.0/28")
		g.Expect(valid).To(BeTrue())
		g.Expect(err).To(BeNil())
	})

	t.Run("different private range restrictions", func(t *testing.T) {
		// 10.x primary with 172.16.x secondary
		valid, err := ValidSecondaryCidr("10.0.0.0/16", []string{}, "172.16.0.0/16")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError(ContainSubstring("the CIDR is restricted")))

		// 10.x primary with 192.168.x secondary
		valid, err = ValidSecondaryCidr("10.0.0.0/16", []string{}, "192.168.0.0/16")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError(ContainSubstring("the CIDR is restricted")))

		// 172.16.x primary with 10.x secondary
		valid, err = ValidSecondaryCidr("172.16.0.0/16", []string{}, "10.1.0.0/16")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError(ContainSubstring("the CIDR is restricted")))

		// 192.168.x primary with 10.x secondary
		valid, err = ValidSecondaryCidr("192.168.0.0/16", []string{}, "10.1.0.0/16")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError(ContainSubstring("the CIDR is restricted")))

		valid, err = ValidSecondaryCidr("172.16.8.0/21", []string{}, "10.0.0.0/16")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError(ContainSubstring("the CIDR is restricted")))
	})

	t.Run("restricted AWS ranges", func(t *testing.T) {
		// 172.16.0.0/12 primary with 172.31.0.0/16 secondary (should be restricted)
		valid, err := ValidSecondaryCidr("172.16.8.0/21", []string{}, "172.31.0.0/16")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError("secondary CIDR is in a reserved or restricted range"))

		// 10.0.0.0/8 primary with existing 10.1.0.0/16 secondary, trying to add 10.0.0.0/16 (should be restricted)
		valid, err = ValidSecondaryCidr("10.255.128.0/17", []string{"10.1.0.0/17"}, "10.0.0.0/16")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError("secondary CIDR is in a reserved or restricted range"))

		// Valid cases that should NOT be restricted
		// 172.16.0.0/12 primary with 172.17.0.0/16 secondary (should be valid)
		valid, err = ValidSecondaryCidr("172.16.8.0/21", []string{}, "172.17.0.0/16")
		g.Expect(valid).To(BeTrue())
		g.Expect(err).To(BeNil())
	})

	t.Run("overlap with primary", func(t *testing.T) {
		// New CIDR contains primary
		valid, err := ValidSecondaryCidr("10.0.0.0/16", []string{}, "10.0.0.0/20")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError("new secondary CIDR overlaps with the primary CIDR"))

		// Primary contains new CIDR
		valid, err = ValidSecondaryCidr("10.0.0.0/8", []string{}, "10.0.0.0/16")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError("new secondary CIDR overlaps with the primary CIDR"))

		// Same CIDR
		valid, err = ValidSecondaryCidr("10.0.0.0/16", []string{}, "10.0.0.0/16")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError("new secondary CIDR overlaps with the primary CIDR"))
	})

	t.Run("overlap with existing secondary", func(t *testing.T) {
		// Invalid existing secondary CIDR
		valid, err := ValidSecondaryCidr("10.0.0.0/16", []string{"invalid-cidr"}, "10.1.0.0/16")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError(ContainSubstring("invalid existing secondary CIDR")))

		// Overlap with existing secondary
		valid, err = ValidSecondaryCidr("10.0.0.0/16", []string{"10.1.0.0/16"}, "10.1.0.0/16")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError("new secondary CIDR overlaps with existing secondary CIDR \"10.1.0.0/16\""))

		// New CIDR contains existing secondary
		valid, err = ValidSecondaryCidr("10.0.0.0/16", []string{"10.1.0.0/16"}, "10.1.0.0/16")
		g.Expect(valid).To(BeFalse())
		g.Expect(err).To(MatchError("new secondary CIDR overlaps with existing secondary CIDR \"10.1.0.0/16\""))
	})

	t.Run("edge cases", func(t *testing.T) {
		// Empty existing secondary CIDRs
		valid, err := ValidSecondaryCidr("10.0.0.0/16", []string{}, "10.1.0.0/16")
		g.Expect(valid).To(BeTrue())
		g.Expect(err).To(BeNil())

		// Multiple existing secondary CIDRs
		valid, err = ValidSecondaryCidr("10.0.0.0/16", []string{"10.1.0.0/16", "10.2.0.0/16"}, "10.3.0.0/16")
		g.Expect(valid).To(BeTrue())
		g.Expect(err).To(BeNil())

		// Different private ranges (172.16)
		valid, err = ValidSecondaryCidr("172.16.0.0/16", []string{"172.17.0.0/16"}, "172.18.0.0/16")
		g.Expect(valid).To(BeTrue())
		g.Expect(err).To(BeNil())

		// 192.168 range
		valid, err = ValidSecondaryCidr("192.168.0.0/16", []string{"192.168.1.0/24"}, "192.168.2.0/24")
		g.Expect(valid).To((BeFalse()))
		g.Expect(err).To(MatchError("new secondary CIDR overlaps with the primary CIDR"))
	})
}

func TestCidrContains(t *testing.T) {
	g := NewGomegaWithT(t)

	t.Run("valid containment cases", func(t *testing.T) {
		// Parent contains child - same network class
		contains, err := CidrContains("10.0.0.0/16", "10.0.1.0/24")
		g.Expect(contains).To(BeTrue())
		g.Expect(err).To(BeNil())

		// Parent contains child - different network classes
		contains, err = CidrContains("10.0.0.0/8", "10.1.0.0/16")
		g.Expect(contains).To(BeTrue())
		g.Expect(err).To(BeNil())

		// Parent contains child - edge case with /32
		contains, err = CidrContains("10.0.0.0/24", "10.0.0.1/32")
		g.Expect(contains).To(BeTrue())
		g.Expect(err).To(BeNil())

		// Parent contains child - edge case with /31
		contains, err = CidrContains("10.0.0.0/24", "10.0.0.0/31")
		g.Expect(contains).To(BeTrue())
		g.Expect(err).To(BeNil())

		// Parent contains child - exact match
		contains, err = CidrContains("10.0.0.0/16", "10.0.0.0/16")
		g.Expect(contains).To(BeTrue())
		g.Expect(err).To(BeNil())

		// Parent contains child - 172.16 range
		contains, err = CidrContains("172.16.0.0/12", "172.16.1.0/24")
		g.Expect(contains).To(BeTrue())
		g.Expect(err).To(BeNil())

		// Parent contains child - 192.168 range
		contains, err = CidrContains("192.168.0.0/16", "192.168.1.0/24")
		g.Expect(contains).To(BeTrue())
		g.Expect(err).To(BeNil())

		// Parent contains child - public IP ranges
		contains, err = CidrContains("203.0.113.0/24", "203.0.113.128/25")
		g.Expect(contains).To(BeTrue())
		g.Expect(err).To(BeNil())

		contains, err = CidrContains("10.0.0.0/24", "10.0.0.128/25")
		g.Expect(contains).To(BeTrue())
		g.Expect(err).To(BeNil())

		contains, err = CidrContains("172.16.16.0/21", "172.16.16.0/23")
		g.Expect(contains).To(BeTrue())
		g.Expect(err).To(BeNil())

		contains, err = CidrContains("172.16.16.0/21", "172.16.18.0/23")
		g.Expect(contains).To(BeTrue())
		g.Expect(err).To(BeNil())

		contains, err = CidrContains("172.16.16.0/21", "172.16.20.0/23")
		g.Expect(contains).To(BeTrue())
		g.Expect(err).To(BeNil())
	})

	t.Run("non-containment cases", func(t *testing.T) {
		// Child contains parent
		contains, err := CidrContains("10.0.1.0/24", "10.0.0.0/16")
		g.Expect(contains).To(BeFalse())
		g.Expect(err).To(BeNil())

		// Completely different networks
		contains, err = CidrContains("10.0.0.0/16", "10.1.0.0/16")
		g.Expect(contains).To(BeFalse())
		g.Expect(err).To(BeNil())

		// Different private ranges
		contains, err = CidrContains("10.0.0.0/16", "172.16.0.0/16")
		g.Expect(contains).To(BeFalse())
		g.Expect(err).To(BeNil())

		// Overlapping but not containing
		contains, err = CidrContains("10.0.0.0/24", "10.0.0.128/23")
		g.Expect(contains).To(BeFalse())
		g.Expect(err).To(BeNil())

		// Adjacent networks
		contains, err = CidrContains("10.0.0.0/24", "10.0.1.0/24")
		g.Expect(contains).To(BeFalse())
		g.Expect(err).To(BeNil())

		// Child starts outside parent
		contains, err = CidrContains("10.0.0.0/24", "10.0.1.0/24")
		g.Expect(contains).To(BeFalse())
		g.Expect(err).To(BeNil())

		// Child ends outside parent
		contains, err = CidrContains("10.0.0.0/24", "10.0.0.0/23")
		g.Expect(contains).To(BeFalse())
		g.Expect(err).To(BeNil())
	})

	t.Run("invalid CIDR format", func(t *testing.T) {
		// Invalid parent CIDR
		contains, err := CidrContains("invalid-cidr", "10.0.1.0/24")
		g.Expect(contains).To(BeFalse())
		g.Expect(err).To(Not(BeNil()))

		// Invalid child CIDR
		contains, err = CidrContains("10.0.0.0/16", "invalid-cidr")
		g.Expect(contains).To(BeFalse())
		g.Expect(err).To(Not(BeNil()))

		// Both invalid
		contains, err = CidrContains("invalid-parent", "invalid-child")
		g.Expect(contains).To(BeFalse())
		g.Expect(err).To(Not(BeNil()))

		// Empty strings
		contains, err = CidrContains("", "10.0.1.0/24")
		g.Expect(contains).To(BeFalse())
		g.Expect(err).To(Not(BeNil()))

		contains, err = CidrContains("10.0.0.0/16", "")
		g.Expect(contains).To(BeFalse())
		g.Expect(err).To(Not(BeNil()))
	})

	t.Run("edge cases", func(t *testing.T) {
		// Single IP in parent
		contains, err := CidrContains("10.0.0.0/24", "10.0.0.1/32")
		g.Expect(contains).To(BeTrue())
		g.Expect(err).To(BeNil())

		// Single IP not in parent
		contains, err = CidrContains("10.0.0.0/24", "10.0.1.1/32")
		g.Expect(contains).To(BeFalse())
		g.Expect(err).To(BeNil())

		// Very large parent, small child
		contains, err = CidrContains("0.0.0.0/0", "10.0.0.0/8")
		g.Expect(contains).To(BeTrue())
		g.Expect(err).To(BeNil())

		// Very small parent, large child
		contains, err = CidrContains("10.0.0.0/32", "10.0.0.0/24")
		g.Expect(contains).To(BeFalse())
		g.Expect(err).To(BeNil())

		// Broadcast address handling
		contains, err = CidrContains("10.0.0.0/24", "10.0.0.255/32")
		g.Expect(contains).To(BeTrue())
		g.Expect(err).To(BeNil())

		// Network address handling
		contains, err = CidrContains("10.0.0.0/24", "10.0.0.0/32")
		g.Expect(contains).To(BeTrue())
		g.Expect(err).To(BeNil())
	})

	t.Run("complex scenarios", func(t *testing.T) {
		// Multiple levels of containment
		contains, err := CidrContains("10.0.0.0/8", "10.1.0.0/16")
		g.Expect(contains).To(BeTrue())
		g.Expect(err).To(BeNil())

		contains, err = CidrContains("10.1.0.0/16", "10.1.1.0/24")
		g.Expect(contains).To(BeTrue())
		g.Expect(err).To(BeNil())

		// Transitive containment should work
		contains, err = CidrContains("10.0.0.0/8", "10.1.1.0/24")
		g.Expect(contains).To(BeTrue())
		g.Expect(err).To(BeNil())

		// Partial overlap (not containment)
		contains, err = CidrContains("10.0.0.0/24", "10.0.0.128/23")
		g.Expect(contains).To(BeFalse())
		g.Expect(err).To(BeNil())

		// Child starts at parent's end but extends beyond
		contains, err = CidrContains("10.0.0.0/24", "10.0.0.0/23")
		g.Expect(contains).To(BeFalse())
		g.Expect(err).To(BeNil())
	})
}
