package argohandler

import (
	"context"
	"fmt"
	"log"
	"math/rand"
	"sync"
	"time"

	"github.com/argoproj/argo-cd/v2/pkg/apiclient"
	"github.com/argoproj/argo-cd/v2/pkg/apiclient/application"
	argoprojv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type ApplicationValues struct {
	Name      string `yaml:"name"`
	Namespace string `yaml:"namespace"`
	Path      string `yaml:"path"`
	Cluster   string `yaml:"cluster"`
	RepoURL   string `yaml:"repoURL"`
	Server    string `yaml:"server"`
	UserID    string `yaml:"userID"`
}

var (
	client apiclient.Client
	once   sync.Once
	err    error
)

func InitializeClient() {
	once.Do(func() {
		argocdServer := "192.168.2.172:32200"
		argocdToken := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhcmdvY2QiLCJzdWIiOiJhZG1pbjphcGlLZXkiLCJuYmYiOjE3NDM4NTAwNzIsImlhdCI6MTc0Mzg1MDA3MiwianRpIjoiNWZhNmQ5MDgtMzljNi00ZWQ4LWE5YzgtMzI4YzMzYjkyNzk4In0.ZvhJk4L5vBQldtJyReKYXCQCWF8j8gHLZlY8PninSFA"

		config := apiclient.ClientOptions{
			ServerAddr: argocdServer,
			AuthToken:  argocdToken,
			Insecure:   true,
		}

		// Initialize the ArgoCD client
		client, err = apiclient.NewClient(&config)
		if err != nil {
			log.Fatalf("Failed to create ArgoCD client: %v", err)
		}
	})
}

func generateRandomString(length int) string {
	const charset = "abcdefghijklmnopqrstuvwxyz0123456789"
	seededRand := rand.New(rand.NewSource(time.Now().UnixNano()))
	b := make([]byte, length)
	for i := range b {
		b[i] = charset[seededRand.Intn(len(charset))]
	}
	return string(b)
}

func CreateApp(clustername string, ControlPlane string, PlatformVersion string, Cpu string, Memory string, userID string) {

	InitializeClient()

	// Create an application client
	_, appClient, err := client.NewApplicationClient()
	if err != nil {
		log.Fatalf("Failed to create application client: %v", err)
	}

	// Generate unique cluster name with user prefix
	uniqueClusterName := clustername

	app := ApplicationValues{
		Name:      uniqueClusterName,
		Namespace: "ns-" + generateRandomString(4),
		Path:      "vcluster-0.21.1",
		Cluster:   "schoobus-onsite",
		Server:    "https://kubernetes.default.svc",
		RepoURL:   "http://192.168.2.20:8015/root/application.git",
		UserID:    userID,
	}

	// Define the ArgoCD application
	argoApp := &argoprojv1alpha1.Application{
		ObjectMeta: metav1.ObjectMeta{
			Name:      app.Name,
			Namespace: "argocd",
			Labels: map[string]string{
				"user-id": userID,
				"type":    "vcluster",
			},
			Finalizers: []string{
				"resources-finalizer.argocd.argoproj.io",
			},
		},
		Spec: argoprojv1alpha1.ApplicationSpec{
			Destination: argoprojv1alpha1.ApplicationDestination{
				Namespace: app.Namespace,
				Name:      app.Cluster,
			},
			Source: &argoprojv1alpha1.ApplicationSource{
				Path:           app.Path,
				RepoURL:        app.RepoURL,
				TargetRevision: "HEAD",
				Helm: &argoprojv1alpha1.ApplicationSourceHelm{
					Parameters: []argoprojv1alpha1.HelmParameter{
						{
							Name:  "controlPlane.advanced.defaultImageRegistry",
							Value: "192.168.2.43:31898",
						},
						{
							Name:  "controlPlane.distro.k8s.version",
							Value: PlatformVersion,
						},
						{
							Name:  "controlPlane.distro.k8s.resources.limits.cpu",
							Value: Cpu,
						},
						{
							Name:  "controlPlane.distro.k8s.resources.limits.memory",
							Value: Memory + "Mi",
						},
						{
							Name:  "controlPlane.distro.k8s.resources.requests.cpu",
							Value: Cpu,
						},
						{
							Name:  "controlPlane.distro.k8s.resources.requests.memory",
							Value: Memory,
						},
					},
				},
			},
			Project: "default",
			SyncPolicy: &argoprojv1alpha1.SyncPolicy{
				SyncOptions: []string{"CreateNamespace=true"},
			},
		},
	}

	// Create the application
	createdApp, err := appClient.Create(context.Background(), &application.ApplicationCreateRequest{
		Application: argoApp,
	})
	if err != nil {
		log.Fatalf("Failed to create application: %v", err)
	}

	fmt.Printf("Application created: %s\n", createdApp.Name)

	SyncApp(app.Name)
}

func SyncApp(appName string) {

	InitializeClient()

	// Create an application client
	_, appClient, err := client.NewApplicationClient()
	if err != nil {
		log.Fatalf("Failed to create application client: %v", err)
	}

	// Synchronize the application
	_, err = appClient.Sync(context.Background(), &application.ApplicationSyncRequest{
		Name: &appName,
	})

	if err != nil {
		log.Fatalf("Failed to sync application: %v", err)
	}

	fmt.Printf("Application synced successfully: %s\n", appName)

}

func ListUserClusters(userID string) ([]string, error) {
	InitializeClient()

	_, appClient, err := client.NewApplicationClient()
	if err != nil {
		return nil, fmt.Errorf("failed to create application client: %v", err)
	}

	// List all applications with the user's label
	selector := fmt.Sprintf("user-id=%s", userID)
	apps, err := appClient.List(context.Background(), &application.ApplicationQuery{
		Selector: &selector,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to list applications: %v", err)
	}

	var clusterNames []string
	for _, app := range apps.Items {
		clusterNames = append(clusterNames, app.Name)
	}

	return clusterNames, nil
}
