package discovery

import (
	"context"
	"fmt"

	"github.com/pkg/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

// KubernetesDiscoveryAgent discovers Kubernetes resources
type KubernetesDiscoveryAgent struct {
	client kubernetes.Interface
	site   string
	region string
}

// NewKubernetesDiscoveryAgent creates a new Kubernetes discovery agent
func NewKubernetesDiscoveryAgent(client kubernetes.Interface, site, region string) *KubernetesDiscoveryAgent {
	return &KubernetesDiscoveryAgent{
		client: client,
		site:   site,
		region: region,
	}
}

// DiscoverNamespaces discovers all namespaces
func (a *KubernetesDiscoveryAgent) DiscoverNamespaces(ctx context.Context) ([]DiscoveredResource, error) {
	namespaces, err := a.client.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
	if err != nil {
		return nil, errors.Wrap(err, "failed to list namespaces")
	}

	var resources []DiscoveredResource
	for _, ns := range namespaces.Items {
		resources = append(resources, DiscoveredResource{
			ResourceType: "NAMESPACE",
			Provider:     "KUBERNETES",
			ProviderID:   fmt.Sprintf("namespace:%s", ns.Name),
			Name:         ns.Name,
			Region:       a.region,
			SiteID:       a.site,
			Metadata: map[string]interface{}{
				"uid":         string(ns.UID),
				"labels":      ns.Labels,
				"annotations": ns.Annotations,
				"phase":       string(ns.Status.Phase),
			},
			Tags: getTagsFromLabels(ns.Labels),
		})
	}

	return resources, nil
}

// DiscoverDeployments discovers all deployments
func (a *KubernetesDiscoveryAgent) DiscoverDeployments(ctx context.Context) ([]DiscoveredResource, error) {
	namespaces, err := a.client.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
	if err != nil {
		return nil, errors.Wrap(err, "failed to list namespaces")
	}

	var resources []DiscoveredResource
	for _, ns := range namespaces.Items {
		deployments, err := a.client.AppsV1().Deployments(ns.Name).List(ctx, metav1.ListOptions{})
		if err != nil {
			continue // Skip namespaces we can't access
		}

		for _, dep := range deployments.Items {
			resources = append(resources, DiscoveredResource{
				ResourceType: "DEPLOYMENT",
				Provider:     "KUBERNETES",
				ProviderID:   fmt.Sprintf("deployment:%s:%s", ns.Name, dep.Name),
				Name:         dep.Name,
				Region:       a.region,
				SiteID:       a.site,
				Metadata: map[string]interface{}{
					"namespace":   ns.Name,
					"uid":         string(dep.UID),
					"replicas":    dep.Spec.Replicas,
					"readyReplicas": dep.Status.ReadyReplicas,
					"labels":      dep.Labels,
					"annotations": dep.Annotations,
				},
				Tags: getTagsFromLabels(dep.Labels),
			})
		}
	}

	return resources, nil
}

// DiscoverServices discovers all services
func (a *KubernetesDiscoveryAgent) DiscoverServices(ctx context.Context) ([]DiscoveredResource, error) {
	namespaces, err := a.client.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
	if err != nil {
		return nil, errors.Wrap(err, "failed to list namespaces")
	}

	var resources []DiscoveredResource
	for _, ns := range namespaces.Items {
		services, err := a.client.CoreV1().Services(ns.Name).List(ctx, metav1.ListOptions{})
		if err != nil {
			continue
		}

		for _, svc := range services.Items {
			resources = append(resources, DiscoveredResource{
				ResourceType: "SERVICE",
				Provider:     "KUBERNETES",
				ProviderID:   fmt.Sprintf("service:%s:%s", ns.Name, svc.Name),
				Name:         svc.Name,
				Region:       a.region,
				SiteID:       a.site,
				Metadata: map[string]interface{}{
					"namespace":   ns.Name,
					"uid":         string(svc.UID),
					"type":        string(svc.Spec.Type),
					"clusterIP":   svc.Spec.ClusterIP,
					"ports":       svc.Spec.Ports,
					"labels":      svc.Labels,
					"annotations": svc.Annotations,
				},
				Tags: getTagsFromLabels(svc.Labels),
			})
		}
	}

	return resources, nil
}

// DiscoverIngress discovers all ingress resources
func (a *KubernetesDiscoveryAgent) DiscoverIngress(ctx context.Context) ([]DiscoveredResource, error) {
	namespaces, err := a.client.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
	if err != nil {
		return nil, errors.Wrap(err, "failed to list namespaces")
	}

	var resources []DiscoveredResource
	for _, ns := range namespaces.Items {
		ingresses, err := a.client.NetworkingV1().Ingresses(ns.Name).List(ctx, metav1.ListOptions{})
		if err != nil {
			continue
		}

		for _, ing := range ingresses.Items {
			resources = append(resources, DiscoveredResource{
				ResourceType: "INGRESS",
				Provider:     "KUBERNETES",
				ProviderID:   fmt.Sprintf("ingress:%s:%s", ns.Name, ing.Name),
				Name:         ing.Name,
				Region:       a.region,
				SiteID:       a.site,
				Metadata: map[string]interface{}{
					"namespace":   ns.Name,
					"uid":         string(ing.UID),
					"hosts":       ing.Spec.Rules,
					"tls":         ing.Spec.TLS,
					"labels":      ing.Labels,
					"annotations": ing.Annotations,
				},
				Tags: getTagsFromLabels(ing.Labels),
			})
		}
	}

	return resources, nil
}

// DiscoverAll discovers all Kubernetes resources
func (a *KubernetesDiscoveryAgent) DiscoverAll(ctx context.Context) ([]DiscoveredResource, error) {
	var allResources []DiscoveredResource

	// Discover namespaces
	namespaces, err := a.DiscoverNamespaces(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "failed to discover namespaces")
	}
	allResources = append(allResources, namespaces...)

	// Discover deployments
	deployments, err := a.DiscoverDeployments(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "failed to discover deployments")
	}
	allResources = append(allResources, deployments...)

	// Discover services
	services, err := a.DiscoverServices(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "failed to discover services")
	}
	allResources = append(allResources, services...)

	// Discover ingress
	ingresses, err := a.DiscoverIngress(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "failed to discover ingress")
	}
	allResources = append(allResources, ingresses...)

	return allResources, nil
}

// getTagsFromLabels extracts tags from Kubernetes labels
func getTagsFromLabels(labels map[string]string) []string {
	var tags []string
	for key, value := range labels {
		if key == "tags" || key == "tag" {
			// If label value is comma-separated, split it
			// Otherwise, use key=value format
			tags = append(tags, value)
		} else if key == "app" || key == "component" || key == "environment" {
			tags = append(tags, fmt.Sprintf("%s:%s", key, value))
		}
	}
	return tags
}

