package aws

import (
	"context"
	"errors"
	"fmt"

	coremodel "security-center-backend/internal/model"
	"security-center-backend/internal/modules/caasm/collector"
	model "security-center-backend/internal/modules/caasm/model"

	"github.com/aws/aws-sdk-go-v2/service/eks"
)

func (c *AWSCollector) collectEKS(ctx context.Context, region string, result *collector.AssetResult) error {
	svc := eks.NewFromConfig(c.cfg, func(o *eks.Options) {
		o.Region = region
	})

	paginator := eks.NewListClustersPaginator(svc, &eks.ListClustersInput{})
	var errs []error
	for paginator.HasMorePages() {
		listResp, err := paginator.NextPage(ctx)
		if err != nil {
			return fmt.Errorf("failed to list EKS clusters: %v", err)
		}
		for _, clusterName := range listResp.Clusters {
			if err := c.collectEKSCluster(ctx, svc, clusterName, region, result); err != nil {
				errs = append(errs, fmt.Errorf("cluster %s: %w", clusterName, err))
			}
		}
	}

	return errors.Join(errs...)
}

func (c *AWSCollector) collectEKSCluster(ctx context.Context, svc *eks.Client, clusterName, region string, result *collector.AssetResult) error {
	// Describe cluster
	descResp, err := svc.DescribeCluster(ctx, &eks.DescribeClusterInput{
		Name: &clusterName,
	})
	if err != nil {
		return err
	}

	if descResp.Cluster == nil {
		return nil
	}

	cluster := descResp.Cluster
	clusterArn := ""
	if cluster.Arn != nil {
		clusterArn = *cluster.Arn
	}

	// Extract cluster endpoint
	endpoint := ""
	if cluster.Endpoint != nil {
		endpoint = *cluster.Endpoint
	}

	// Determine if endpoint is public, private, or both
	endpointPublic := false
	endpointPrivate := false
	endpointAccessType := "unknown"

	if cluster.ResourcesVpcConfig != nil {
		endpointPublic = cluster.ResourcesVpcConfig.EndpointPublicAccess
		endpointPrivate = cluster.ResourcesVpcConfig.EndpointPrivateAccess

		if endpointPublic && endpointPrivate {
			endpointAccessType = "public-and-private"
		} else if endpointPublic {
			endpointAccessType = "public"
		} else if endpointPrivate {
			endpointAccessType = "private"
		}
	}

	// Extract security group IDs
	securityGroupIds := make([]string, 0)
	if cluster.ResourcesVpcConfig != nil && cluster.ResourcesVpcConfig.SecurityGroupIds != nil {
		securityGroupIds = cluster.ResourcesVpcConfig.SecurityGroupIds
	}

	// Extract VPC config
	vpcId := ""
	subnetIds := make([]string, 0)
	if cluster.ResourcesVpcConfig != nil {
		if cluster.ResourcesVpcConfig.VpcId != nil {
			vpcId = *cluster.ResourcesVpcConfig.VpcId
		}
		if cluster.ResourcesVpcConfig.SubnetIds != nil {
			subnetIds = cluster.ResourcesVpcConfig.SubnetIds
		}
	}

	// Create EKS cluster asset
	asset := c.newBaseAsset(clusterArn, clusterName, string(model.AssetTypeCompute), "active", region)
	c.setService(&asset, "eks")

	meta := map[string]interface{}{
		"cluster_name":       clusterName,
		"cluster_arn":        clusterArn,
		"endpoint":           endpoint,
		"endpoint_access":    endpointAccessType,
		"endpoint_public":    endpointPublic,
		"endpoint_private":   endpointPrivate,
		"version":            cluster.Version,
		"platform_version":   cluster.PlatformVersion,
		"status":             cluster.Status,
		"vpc_id":             vpcId,
		"subnet_ids":         subnetIds,
		"security_group_ids": securityGroupIds,
		"created_at":         cluster.CreatedAt,
	}

	// Add OIDC provider if exists
	if cluster.Identity != nil && cluster.Identity.Oidc != nil && cluster.Identity.Oidc.Issuer != nil {
		meta["oidc_issuer"] = *cluster.Identity.Oidc.Issuer
	}

	asset.Metadata = coremodel.JSONB(meta)
	result.Assets = append(result.Assets, asset)

	// Add cluster endpoint as domain asset if available
	if endpoint != "" {
		domainAsset := model.DomainAsset{
			Asset:       model.Asset{NativeID: asset.NativeID},
			DomainName:  endpoint,
			DomainClass: model.DomainClassServiceHostname,
			Registrar:   "eks",
		}
		setServiceEndpointDomainDefaults(&domainAsset, "eks", endpointPublic)
		if endpointPublic && endpointPrivate {
			domainAsset.ZoneType = "public-and-private"
		}
		c.setService(&domainAsset.Asset, "eks")
		result.DomainAssets = append(result.DomainAssets, domainAsset)

		// Kubernetes API endpoint is always HTTPS on port 443
		exposure := "private"
		if endpointPublic {
			exposure = "public"
		}

		result.Ports = append(result.Ports, model.IPPortIndex{
			Asset:             model.Asset{NativeID: asset.NativeID},
			IPAddress:         endpoint,
			Port:              443,
			Protocol:          "tcp",
			ObservationSource: model.PortObservationSourceServiceEndpoint,
			ServiceName:       "kubernetes-api",
			Exposure:          exposure,
			Status:            "open",
		})
	}

	// Collect node groups
	if err := c.collectEKSNodeGroups(ctx, svc, clusterName, region, result); err != nil {
		return err
	}

	return nil
}

func (c *AWSCollector) collectEKSNodeGroups(ctx context.Context, svc *eks.Client, clusterName, region string, result *collector.AssetResult) error {
	paginator := eks.NewListNodegroupsPaginator(svc, &eks.ListNodegroupsInput{
		ClusterName: &clusterName,
	})
	var errs []error
	for paginator.HasMorePages() {
		listResp, err := paginator.NextPage(ctx)
		if err != nil {
			return err
		}
		for _, ngName := range listResp.Nodegroups {
			// Describe node group
			descResp, err := svc.DescribeNodegroup(ctx, &eks.DescribeNodegroupInput{
				ClusterName:   &clusterName,
				NodegroupName: &ngName,
			})
			if err != nil {
				errs = append(errs, fmt.Errorf("nodegroup %s: %w", ngName, err))
				continue
			}

			if descResp.Nodegroup == nil {
				continue
			}

			ng := descResp.Nodegroup
			ngArn := ""
			if ng.NodegroupArn != nil {
				ngArn = *ng.NodegroupArn
			}

			// Create node group asset
			asset := c.newBaseAsset(ngArn, ngName, string(model.AssetTypeCompute), "active", region)
			c.setService(&asset, "eks")

			meta := map[string]interface{}{
				"nodegroup_name": ngName,
				"nodegroup_arn":  ngArn,
				"cluster_name":   clusterName,
				"status":         ng.Status,
				"capacity_type":  ng.CapacityType,
				"instance_types": ng.InstanceTypes,
				"ami_type":       ng.AmiType,
				"disk_size":      ng.DiskSize,
				"created_at":     ng.CreatedAt,
				"modified_at":    ng.ModifiedAt,
			}

			if ng.ScalingConfig != nil {
				meta["desired_size"] = ng.ScalingConfig.DesiredSize
				meta["min_size"] = ng.ScalingConfig.MinSize
				meta["max_size"] = ng.ScalingConfig.MaxSize
			}

			if ng.Subnets != nil {
				meta["subnet_ids"] = ng.Subnets
			}

			asset.Metadata = coremodel.JSONB(meta)
			result.Assets = append(result.Assets, asset)
		}
	}

	return errors.Join(errs...)
}
