package aws

import (
	"context"
	"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/rds"
)

func (c *AWSCollector) collectRDS(ctx context.Context, region string, result *collector.AssetResult) error {
	svc := rds.NewFromConfig(c.cfg, func(o *rds.Options) {
		o.Region = region
	})
	paginator := rds.NewDescribeDBInstancesPaginator(svc, &rds.DescribeDBInstancesInput{})

	for paginator.HasMorePages() {
		page, err := paginator.NextPage(ctx)
		if err != nil {
			return err
		}

		for _, db := range page.DBInstances {
			name := *db.DBInstanceIdentifier
			status := *db.DBInstanceStatus

			// Base Asset
			asset := c.newBaseAsset(*db.DBInstanceArn, name, string(model.AssetTypeDatabase), status, region)
			c.setService(&asset, "rds")

			// Extract tags and set Environment/Criticality
			tags := c.getRDSTags(ctx, svc, *db.DBInstanceArn)
			asset.Environment = c.extractEnvironment(tags)
			asset.Criticality = c.extractCriticality(tags)

			// Extract port
			var port uint32
			if db.Endpoint != nil && db.Endpoint.Port != nil {
				port = uint32(*db.Endpoint.Port)
			}

			// Extract security groups
			sgIDs := make([]string, 0, len(db.VpcSecurityGroups))
			for _, sg := range db.VpcSecurityGroups {
				if sg.VpcSecurityGroupId != nil {
					sgIDs = append(sgIDs, *sg.VpcSecurityGroupId)
				}
			}

			meta := map[string]interface{}{
				"engine":              db.Engine,
				"engine_version":      db.EngineVersion,
				"instance_class":      db.DBInstanceClass,
				"publicly_accessible": db.PubliclyAccessible,
				"storage_encrypted":   db.StorageEncrypted,
				"port":                port,
				"security_groups":     sgIDs,
				"multi_az":            db.MultiAZ,
			}
			asset.Metadata = coremodel.JSONB(meta)

			result.Assets = append(result.Assets, asset)

			// Domain Asset (Endpoint)
			var endpointAddr string
			if db.Endpoint != nil && db.Endpoint.Address != nil {
				endpointAddr = *db.Endpoint.Address
				domainAsset := model.DomainAsset{
					Asset:       model.Asset{NativeID: asset.NativeID},
					DomainName:  endpointAddr,
					DomainClass: model.DomainClassServiceHostname,
					Registrar:   "aws",
				}
				isPublic := db.PubliclyAccessible != nil && *db.PubliclyAccessible
				setServiceEndpointDomainDefaults(&domainAsset, "rds", isPublic)
				c.setService(&domainAsset.Asset, "rds")
				result.DomainAssets = append(result.DomainAssets, domainAsset)

				// Add port information if available
				if port > 0 {
					result.Ports = append(result.Ports, model.IPPortIndex{
						Asset:             model.Asset{NativeID: asset.NativeID},
						IPAddress:         endpointAddr,
						Port:              port,
						Protocol:          "tcp",
						ObservationSource: model.PortObservationSourceServiceEndpoint,
						ServiceName:       getDatabaseServiceName(port),
						Exposure:          endpointExposure(isPublic),
						Status:            "open",
					})
				}
			}
		}
	}
	return nil
}

// getRDSTags fetches tags for an RDS instance
func (c *AWSCollector) getRDSTags(ctx context.Context, svc *rds.Client, arn string) map[string]string {
	tags := make(map[string]string)
	out, err := svc.ListTagsForResource(ctx, &rds.ListTagsForResourceInput{
		ResourceName: &arn,
	})
	if err != nil {
		eventType := "warning"
		message := fmt.Sprintf("Failed to retrieve tags for RDS instance: %v", err)
		if downgradedType, downgradedMessage, ok := classifyAWSHelperEvent("rds", "ListTagsForResource", err); ok {
			eventType = downgradedType
			message = downgradedMessage
		}
		c.report(eventType, message, map[string]interface{}{
			"arn":   arn,
			"error": err.Error(),
		})
		return tags
	}

	for _, tag := range out.TagList {
		if tag.Key != nil && tag.Value != nil {
			tags[*tag.Key] = *tag.Value
		}
	}
	return tags
}

// getDatabaseServiceName returns database service names for common database ports
func getDatabaseServiceName(port uint32) string {
	dbServices := map[uint32]string{
		3306:  "mysql",
		5432:  "postgresql",
		1433:  "mssql",
		1521:  "oracle",
		5984:  "couchdb",
		6379:  "redis",
		27017: "mongodb",
		9042:  "cassandra",
	}

	if name, ok := dbServices[port]; ok {
		return name
	}
	return getServiceName(port)
}
