package task

import (
	"context"
	"os"
	"testing"

	"github.com/go-logr/logr"
	. "github.com/onsi/gomega"
	opv1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
	"github.com/stretchr/testify/assert"
	scheduling_v1 "k8s.io/api/scheduling/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/tools/record"
	"k8s.io/utils/ptr"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"
	"sigs.k8s.io/yaml"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/config"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
)

func TestCreate(t *testing.T) {
	type args struct {
		restoreYAMLPath string
		cloud           string
	}
	tests := []struct {
		name     string
		args     args
		wantYAML string
		wantErr  bool
	}{
		{
			name: "aws-snapshot-restore",
			args: args{
				restoreYAMLPath: "testdata/restore_from/aws-snapshot-restore-infra.yaml",
				cloud:           "aws",
			},
			wantYAML: "testdata/restore_expected/aws-snapshot-restore-operator.yaml",
			wantErr:  false,
		},
		{
			name: "aws-log-restore",
			args: args{
				restoreYAMLPath: "testdata/restore_from/aws-log-restore-infra.yaml",
				cloud:           "aws",
			},
			wantYAML: "testdata/restore_expected/aws-log-restore-operator.yaml",
			wantErr:  false,
		},
		{
			name: "azure-snapshot-restore",
			args: args{
				restoreYAMLPath: "testdata/restore_from/azure-snapshot-restore-infra.yaml",
				cloud:           "azure",
			},
			wantYAML: "testdata/restore_expected/azure-snapshot-restore-operator.yaml",
			wantErr:  false,
		},
		{
			name: "azure-log-restore",
			args: args{
				restoreYAMLPath: "testdata/restore_from/azure-log-restore-infra.yaml",
				cloud:           "azure",
			},
			wantYAML: "testdata/restore_expected/azure-log-restore-operator.yaml",
			wantErr:  false,
		},
		{
			name: "gcp-snapshot-restore",
			args: args{
				restoreYAMLPath: "testdata/restore_from/gcp-snapshot-restore-infra.yaml",
				cloud:           "gcp",
			},
			wantYAML: "testdata/restore_expected/gcp-snapshot-restore-operator.yaml",
			wantErr:  false,
		},
		{
			name: "gcp-log-restore",
			args: args{
				restoreYAMLPath: "testdata/restore_from/gcp-log-restore-infra.yaml",
				cloud:           "gcp",
			},
			wantYAML: "testdata/restore_expected/gcp-log-restore-operator.yaml",
			wantErr:  false,
		},
		{
			name: "aws-snapshot-cross-region-restore",
			args: args{
				restoreYAMLPath: "testdata/restore_from/aws-snapshot-cross-region-restore-infra.yaml",
				cloud:           "aws",
			},
			wantYAML: "testdata/restore_expected/aws-snapshot-cross-region-restore-operator.yaml",
			wantErr:  false,
		},
		{
			name: "aws-log-cross-region-restore",
			args: args{
				restoreYAMLPath: "testdata/restore_from/aws-log-cross-region-restore-infra.yaml",
				cloud:           "aws",
			},
			wantYAML: "testdata/restore_expected/aws-log-cross-region-restore-operator.yaml",
			wantErr:  false,
		},
		{
			name: "gcp-snapshot-cross-region-restore",
			args: args{
				restoreYAMLPath: "testdata/restore_from/gcp-snapshot-cross-region-restore-infra.yaml",
				cloud:           "gcp",
			},
			wantYAML: "testdata/restore_expected/gcp-snapshot-cross-region-restore-operator.yaml",
			wantErr:  false,
		},
		{
			name: "gcp-log-cross-region-restore",
			args: args{
				restoreYAMLPath: "testdata/restore_from/gcp-log-cross-region-restore-infra.yaml",
				cloud:           "gcp",
			},
			wantYAML: "testdata/restore_expected/gcp-log-cross-region-restore-operator.yaml",
			wantErr:  false,
		},
		{
			name: "azure-snapshot-cross-region-restore",
			args: args{
				restoreYAMLPath: "testdata/restore_from/azure-snapshot-cross-region-restore-infra.yaml",
				cloud:           "azure",
			},
			wantYAML: "testdata/restore_expected/azure-snapshot-cross-region-restore-operator.yaml",
			wantErr:  false,
		},
		{
			name: "azure-log-cross-region-restore",
			args: args{
				restoreYAMLPath: "testdata/restore_from/azure-log-cross-region-restore-infra.yaml",
				cloud:           "azure",
			},
			wantYAML: "testdata/restore_expected/azure-log-cross-region-restore-operator.yaml",
			wantErr:  false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			g := NewWithT(t)

			// Create a fake client to mock API calls
			s := runtime.NewScheme()
			g.Expect(opv1alpha1.AddToScheme(s)).Should(Succeed())
			g.Expect(cloudv1.SchemeBuilder.AddToScheme(s)).Should(Succeed())
			g.Expect(scheduling_v1.AddToScheme(s)).Should(Succeed())
			fakeApiserver := fake.NewClientBuilder().WithScheme(s).Build()

			// Create a context
			ctx := context.TODO()

			// Create necessary arguments for NewContext
			eventRecorder := &record.FakeRecorder{}
			logger := logr.Discard()
			localCli := fakeApiserver
			remoteCli := fakeApiserver
			restore := &cloudv1.Restore{}

			restoreYaml, err := os.ReadFile(tt.args.restoreYAMLPath)
			if (err != nil) != tt.wantErr {
				t.Errorf("Failed to read restore YAML file: %v", err)
				return
			}

			if err == nil {
				if err = yaml.Unmarshal(restoreYaml, restore); err != nil {
					t.Fatalf("failed to unmarshal YAML: %v", err)
				}
			}

			cluster := &cloudv1.Cluster{
				ObjectMeta: metav1.ObjectMeta{
					Name: restore.Spec.ClusterName,
				},
			}

			snapshotBackupStorage := ptr.Deref(restore.Spec.From.SnapshotBackupStorage, "")
			logBackupStorage := ptr.Deref(restore.Spec.From.LogBackupStorage, "")

			var cloudProvider CloudProvider
			switch tt.args.cloud {
			case "aws":
				cloudProvider, err = NewAWSProvider(
					snapshotBackupStorage,
					logBackupStorage,
					"us-west-2",
					"kubernetesName",
					"kubeVersion",
					"nodeRole",
					"accountID",
					"assumeRoleARN",
					"externalID",
					cloudv1.CloudAWS{},
					"clusterProviderConfigName",
					"tenantID",
					"projectID",
					cluster.GetClusterID(),
					&config.RootConfig{})
			case "azure":
				cloudProvider, err = NewAzureProvider(
					"tenantID",
					"subscriptionID",
					"clientID",
					"dataplaneSubscriptionID",
					"kubernetesName",
					"kubeVersion",
					cloudv1.CloudAzure{},
					"clusterProviderConfigName",
					"tenantID",
					"projectID",
					snapshotBackupStorage,
					logBackupStorage,
				)
			case "gcp":
				cloudProvider, err = NewGCPProvider(
					snapshotBackupStorage,
					logBackupStorage,
					"us-west1",
					"test-gcp-project",
					"kubernetesName",
					"kubeVersion",
					"nodeRole",
					"clusterProviderConfigName",
					"tenantID",
					"projectID",
				)
			}
			if (err != nil) != tt.wantErr {
				t.Fatalf("failed to create cloud provider: %v", err)
			}

			// Initialize taskContext with required arguments
			taskContext := NewContext(
				eventRecorder,
				logger,
				localCli,
				remoteCli,
				restore,
				cluster,
				"env",
				cloudProvider,
			)

			// Create a restoreTask with necessary fields
			task := &restoreTask{
				baseTask: &baseTask{
					Context: taskContext,
				},
			}

			// Call the Create method
			result := task.Create(ctx)

			if tt.wantErr {
				assert.NotEqual(t, deploy.Done(), result)
				return
			}

			// Assert that the result is deploy.Done()
			assert.Equal(t, deploy.Done(), result)

			// Fetch the created Restore object
			opRestore := &opv1alpha1.Restore{}
			err = taskContext.remote.Get(ctx, types.NamespacedName{
				Name:      task.getRestoreName(),
				Namespace: task.clusterNamer.GetTiDBNamespace(),
			}, opRestore)
			g.Expect(err).ToNot(HaveOccurred())

			gotYAML, err := yaml.Marshal(opRestore)
			g.Expect(err).ToNot(HaveOccurred())

			// // generate the expected YAML file
			// err = os.WriteFile(tt.wantYAML, gotYAML, 0644)
			// g.Expect(err).ToNot(HaveOccurred(), "Failed to write generated YAML to file")
			// t.Logf("Generated YAML written to %s", tt.wantYAML)

			// Compare the generated YAML with the expected YAML
			expectedYAML, err := os.ReadFile(tt.wantYAML)
			g.Expect(err).ToNot(HaveOccurred(), "Failed to read expected YAML file")
			t.Log(string(gotYAML))
			g.Expect(string(gotYAML)).To(Equal(string(expectedYAML)))
		})
	}
}
