package k8s

import (
	"context"
	"fmt"

	"github.com/ocdp/cluster-service/internal/domain/entity"
	appsv1 "k8s.io/api/apps/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

// ScaleClient provides K8s-native workload scaling (bypasses Helm)
type ScaleClient struct{}

// NewScaleClient creates a ScaleClient
func NewScaleClient() *ScaleClient {
	return &ScaleClient{}
}

// findDeployment searches for a deployment matching the release name using various label strategies.
func (c *ScaleClient) findDeployment(ctx context.Context, clientset *kubernetes.Clientset, namespace, releaseName string) (*appsv1.Deployment, error) {
	labelQueries := []string{
		fmt.Sprintf("app.kubernetes.io/instance=%s", releaseName),
		fmt.Sprintf("release=%s", releaseName),
		fmt.Sprintf("app=%s", releaseName),
		fmt.Sprintf("app.kubernetes.io/name=%s", releaseName),
	}

	for _, query := range labelQueries {
		deployments, err := clientset.AppsV1().Deployments(namespace).List(ctx, metav1.ListOptions{
			LabelSelector: query,
		})
		if err != nil {
			continue
		}
		if len(deployments.Items) > 0 {
			return &deployments.Items[0], nil
		}
	}

	// Fallback: get by name directly
	dep, err := clientset.AppsV1().Deployments(namespace).Get(ctx, releaseName, metav1.GetOptions{})
	if err == nil && dep != nil {
		return dep, nil
	}

	return nil, nil
}

// GetDeploymentReplicas returns the current replicas count for a deployment.
func (c *ScaleClient) GetDeploymentReplicas(ctx context.Context, cluster *entity.Cluster, namespace, releaseName string) (int32, error) {
	clientset, err := c.clientsetForCluster(cluster)
	if err != nil {
		return 0, fmt.Errorf("failed to create k8s client: %w", err)
	}

	dep, err := c.findDeployment(ctx, clientset, namespace, releaseName)
	if err != nil {
		return 0, err
	}
	if dep != nil && dep.Spec.Replicas != nil {
		return *dep.Spec.Replicas, nil
	}

	// Fallback to statefulsets
	return c.getStatefulSetReplicas(ctx, clientset, namespace, releaseName)
}

func (c *ScaleClient) getStatefulSetReplicas(ctx context.Context, clientset *kubernetes.Clientset, namespace, releaseName string) (int32, error) {
	stsList, err := clientset.AppsV1().StatefulSets(namespace).List(ctx, metav1.ListOptions{
		LabelSelector: fmt.Sprintf("app.kubernetes.io/instance=%s", releaseName),
	})
	if err != nil {
		return 0, err
	}
	if len(stsList.Items) == 0 {
		return 0, nil // No replicable workload found
	}
	sts := stsList.Items[0]
	if sts.Spec.Replicas != nil {
		return *sts.Spec.Replicas, nil
	}
	return 0, nil
}

// ScaleDeployment scales the K8s deployment directly (bypasses Helm).
func (c *ScaleClient) ScaleDeployment(ctx context.Context, cluster *entity.Cluster, namespace, releaseName string, replicas int32) error {
	clientset, err := c.clientsetForCluster(cluster)
	if err != nil {
		return fmt.Errorf("failed to create k8s client: %w", err)
	}

	dep, err := c.findDeployment(ctx, clientset, namespace, releaseName)
	if err != nil {
		return err
	}
	if dep != nil {
		dep.Spec.Replicas = &replicas
		_, err = clientset.AppsV1().Deployments(namespace).Update(ctx, dep, metav1.UpdateOptions{})
		if err != nil {
			return fmt.Errorf("failed to scale deployment %s: %w", dep.Name, err)
		}
		return nil
	}

	// Try StatefulSets
	stsList, err := clientset.AppsV1().StatefulSets(namespace).List(ctx, metav1.ListOptions{
		LabelSelector: fmt.Sprintf("app.kubernetes.io/instance=%s", releaseName),
	})
	if err == nil && len(stsList.Items) > 0 {
		sts := stsList.Items[0]
		sts.Spec.Replicas = &replicas
		_, err = clientset.AppsV1().StatefulSets(namespace).Update(ctx, &sts, metav1.UpdateOptions{})
		if err != nil {
			return fmt.Errorf("failed to scale statefulset %s: %w", sts.Name, err)
		}
		return nil
	}

	return fmt.Errorf("no deployment or statefulset found for release %s in namespace %s", releaseName, namespace)
}

func (c *ScaleClient) clientsetForCluster(cluster *entity.Cluster) (*kubernetes.Clientset, error) {
	restConfig, err := restConfigFromCluster(cluster)
	if err != nil {
		return nil, fmt.Errorf("failed to create rest config: %w", err)
	}
	clientset, err := kubernetes.NewForConfig(restConfig)
	if err != nil {
		return nil, fmt.Errorf("failed to create clientset: %w", err)
	}
	return clientset, nil
}
