package azure

import (
	"context"
	"testing"

	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
	"github.com/stretchr/testify/assert"
	azurenetworkv1beta2 "github.com/upbound/provider-azure/apis/network/v1beta2"
	"k8s.io/utils/ptr"
	"sigs.k8s.io/controller-runtime/pkg/client"

	"github.com/tidbcloud/observability/agent-control-server/pkg/testkits"
)

func makePrivateEndpoint(name, alias, subnet string) *azurenetworkv1beta2.PrivateEndpoint {
	ep := &azurenetworkv1beta2.PrivateEndpoint{}
	ep.Name = name
	ep.Spec.ForProvider.PrivateServiceConnection = &azurenetworkv1beta2.PrivateServiceConnectionParameters{
		PrivateConnectionResourceAlias: ptr.To(alias),
	}
	ep.Spec.ForProvider.SubnetID = ptr.To(subnet)
	ep.Status.SetConditions(xpv1.Available())
	return ep
}

func TestFindReusablePrivateEndpointName(t *testing.T) {
	tests := []struct {
		name     string
		objects  []client.Object
		alias    string
		subnetID string
		wantName string
	}{
		{
			name: "success: matching endpoint found",
			objects: []client.Object{
				makePrivateEndpoint("pe-match", "service-alias", "subnet-1"),
			},
			alias:    "service-alias",
			subnetID: "subnet-1",
			wantName: "pe-match",
		},
		{
			name:     "no endpoints",
			objects:  []client.Object{},
			alias:    "service-alias",
			subnetID: "subnet-1",
			wantName: "",
		},
		{
			name: "mismatched service alias",
			objects: []client.Object{
				makePrivateEndpoint("pe-1", "other-alias", "subnet-1"),
			},
			alias:    "service-alias",
			subnetID: "subnet-1",
			wantName: "",
		},
		{
			name: "mismatched subnet",
			objects: []client.Object{
				makePrivateEndpoint("pe-1", "service-alias", "subnet-2"),
			},
			alias:    "service-alias",
			subnetID: "subnet-1",
			wantName: "",
		},
		{
			name: "endpoint in deleting state",
			objects: []client.Object{func() client.Object {
				ep := makePrivateEndpoint("pe-deleting", "service-alias", "subnet-1")
				ep.Status.SetConditions(xpv1.Deleting())
				return ep
			}()},
			alias:    "service-alias",
			subnetID: "subnet-1",
			wantName: "",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			fakeCli := testkits.BuildClient(tt.objects...)
			gotName, err := FindReusablePrivateEndpointName(context.Background(), fakeCli, tt.alias, tt.subnetID)
			assert.NoError(t, err)
			assert.Equal(t, tt.wantName, gotName)
		})
	}
}

func TestIsPrivateEndpointReusable(t *testing.T) {
	tests := []struct {
		name     string
		endpoint *azurenetworkv1beta2.PrivateEndpoint
		alias    string
		subnetID string
		want     bool
	}{
		{
			name:     "matching and not deleting",
			endpoint: makePrivateEndpoint("pe-1", "service-alias", "subnet-1"),
			alias:    "service-alias",
			subnetID: "subnet-1",
			want:     true,
		},
		{
			name: "nil private service connection",
			endpoint: func() *azurenetworkv1beta2.PrivateEndpoint {
				ep := makePrivateEndpoint("pe-1", "service-alias", "subnet-1")
				ep.Spec.ForProvider.PrivateServiceConnection = nil
				return ep
			}(),
			alias:    "service-alias",
			subnetID: "subnet-1",
			want:     false,
		},
		{
			name: "nil alias",
			endpoint: func() *azurenetworkv1beta2.PrivateEndpoint {
				ep := makePrivateEndpoint("pe-1", "service-alias", "subnet-1")
				ep.Spec.ForProvider.PrivateServiceConnection.PrivateConnectionResourceAlias = nil
				return ep
			}(),
			alias:    "service-alias",
			subnetID: "subnet-1",
			want:     false,
		},
		{
			name:     "wrong alias",
			endpoint: makePrivateEndpoint("pe-1", "other-alias", "subnet-1"),
			alias:    "service-alias",
			subnetID: "subnet-1",
			want:     false,
		},
		{
			name: "nil subnet",
			endpoint: func() *azurenetworkv1beta2.PrivateEndpoint {
				ep := makePrivateEndpoint("pe-1", "service-alias", "subnet-1")
				ep.Spec.ForProvider.SubnetID = nil
				return ep
			}(),
			alias:    "service-alias",
			subnetID: "subnet-1",
			want:     false,
		},
		{
			name:     "wrong subnet",
			endpoint: makePrivateEndpoint("pe-1", "service-alias", "subnet-2"),
			alias:    "service-alias",
			subnetID: "subnet-1",
			want:     false,
		},
		{
			name: "deleting condition",
			endpoint: func() *azurenetworkv1beta2.PrivateEndpoint {
				ep := makePrivateEndpoint("pe-1", "service-alias", "subnet-1")
				ep.Status.SetConditions(xpv1.Deleting())
				return ep
			}(),
			alias:    "service-alias",
			subnetID: "subnet-1",
			want:     false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assert.Equal(t, tt.want, isPrivateEndpointReusable(tt.endpoint, tt.alias, tt.subnetID))
		})
	}
}

func TestExtractPrivateIP(t *testing.T) {
	tests := []struct {
		name     string
		endpoint *azurenetworkv1beta2.PrivateEndpoint
		wantIP   string
	}{
		{
			name: "IP from CustomDNSConfigs",
			endpoint: &azurenetworkv1beta2.PrivateEndpoint{
				Status: azurenetworkv1beta2.PrivateEndpointStatus{
					AtProvider: azurenetworkv1beta2.PrivateEndpointObservation{
						CustomDNSConfigs: []azurenetworkv1beta2.CustomDNSConfigsObservation{{
							IPAddresses: []*string{ptr.To("10.0.0.1")},
						}},
					},
				},
			},
			wantIP: "10.0.0.1",
		},
		{
			name: "IP from PrivateServiceConnection fallback",
			endpoint: &azurenetworkv1beta2.PrivateEndpoint{
				Status: azurenetworkv1beta2.PrivateEndpointStatus{
					AtProvider: azurenetworkv1beta2.PrivateEndpointObservation{
						PrivateServiceConnection: &azurenetworkv1beta2.PrivateServiceConnectionObservation{
							PrivateIPAddress: ptr.To("10.0.0.2"),
						},
					},
				},
			},
			wantIP: "10.0.0.2",
		},
		{
			name: "both present CustomDNSConfigs wins",
			endpoint: &azurenetworkv1beta2.PrivateEndpoint{
				Status: azurenetworkv1beta2.PrivateEndpointStatus{
					AtProvider: azurenetworkv1beta2.PrivateEndpointObservation{
						CustomDNSConfigs: []azurenetworkv1beta2.CustomDNSConfigsObservation{{
							IPAddresses: []*string{ptr.To("10.0.0.1")},
						}},
						PrivateServiceConnection: &azurenetworkv1beta2.PrivateServiceConnectionObservation{
							PrivateIPAddress: ptr.To("10.0.0.2"),
						},
					},
				},
			},
			wantIP: "10.0.0.1",
		},
		{
			name: "no IP anywhere",
			endpoint: &azurenetworkv1beta2.PrivateEndpoint{
				Status: azurenetworkv1beta2.PrivateEndpointStatus{
					AtProvider: azurenetworkv1beta2.PrivateEndpointObservation{
						CustomDNSConfigs: []azurenetworkv1beta2.CustomDNSConfigsObservation{{
							IPAddresses: []*string{ptr.To("")},
						}},
						PrivateServiceConnection: &azurenetworkv1beta2.PrivateServiceConnectionObservation{},
					},
				},
			},
			wantIP: "",
		},
		{
			name: "nil PrivateServiceConnection",
			endpoint: &azurenetworkv1beta2.PrivateEndpoint{
				Status: azurenetworkv1beta2.PrivateEndpointStatus{
					AtProvider: azurenetworkv1beta2.PrivateEndpointObservation{},
				},
			},
			wantIP: "",
		},
		{
			name: "empty IPAddresses fallback to PSC",
			endpoint: &azurenetworkv1beta2.PrivateEndpoint{
				Status: azurenetworkv1beta2.PrivateEndpointStatus{
					AtProvider: azurenetworkv1beta2.PrivateEndpointObservation{
						CustomDNSConfigs: []azurenetworkv1beta2.CustomDNSConfigsObservation{{
							IPAddresses: []*string{},
						}},
						PrivateServiceConnection: &azurenetworkv1beta2.PrivateServiceConnectionObservation{
							PrivateIPAddress: ptr.To("10.0.0.3"),
						},
					},
				},
			},
			wantIP: "10.0.0.3",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assert.Equal(t, tt.wantIP, ExtractPrivateIP(tt.endpoint))
		})
	}
}
