package middlewares

import (
	"github.com/sirupsen/logrus"
	"github.com/snyk/driftctl/pkg/resource"
	"github.com/snyk/driftctl/pkg/resource/aws"
)

// Explodes policy found in aws_api_gateway_rest_api.policy from state resources to dedicated resources
type AwsApiGatewayRestApiPolicyExpander struct {
	resourceFactory resource.ResourceFactory
}

func NewAwsApiGatewayRestApiPolicyExpander(resourceFactory resource.ResourceFactory) AwsApiGatewayRestApiPolicyExpander {
	return AwsApiGatewayRestApiPolicyExpander{
		resourceFactory: resourceFactory,
	}
}

func (m AwsApiGatewayRestApiPolicyExpander) Execute(_, resourcesFromState *[]*resource.Resource) error {
	newList := make([]*resource.Resource, 0)
	for _, res := range *resourcesFromState {
		// Ignore all resources other than api_gateway_rest_api
		if res.ResourceType() != aws.AwsApiGatewayRestApiResourceType {
			newList = append(newList, res)
			continue
		}

		newList = append(newList, res)

		if hasRestApiPolicyAttached(res.ResourceId(), resourcesFromState) {
			res.Attrs.SafeDelete([]string{"policy"})
			continue
		}

		err := m.handlePolicy(res, &newList)
		if err != nil {
			return err
		}
	}
	*resourcesFromState = newList
	return nil
}

func (m *AwsApiGatewayRestApiPolicyExpander) handlePolicy(api *resource.Resource, results *[]*resource.Resource) error {
	policy, exist := api.Attrs.Get("policy")
	if !exist || policy == nil || policy == "" {
		return nil
	}

	data := map[string]interface{}{
		"id":          api.ResourceId(),
		"rest_api_id": api.ResourceId(),
		"policy":      policy,
	}

	newPolicy := m.resourceFactory.CreateAbstractResource(aws.AwsApiGatewayRestApiPolicyResourceType, api.ResourceId(), data)

	*results = append(*results, newPolicy)
	logrus.WithFields(logrus.Fields{
		"id": newPolicy.ResourceId(),
	}).Debug("Created new policy from api gateway rest api")

	api.Attrs.SafeDelete([]string{"policy"})
	return nil
}

// Return true if the rest api has a aws_api_gateway_rest_api_policy resource attached to itself.
// It is mandatory since it's possible to have a aws_api_gateway_rest_api with an inline policy
// AND a aws_api_gateway_rest_api_policy resource at the same time. At the end, on the AWS console,
// the aws_api_gateway_rest_api_policy will be used.
func hasRestApiPolicyAttached(api string, resourcesFromState *[]*resource.Resource) bool {
	for _, res := range *resourcesFromState {
		if res.ResourceType() == aws.AwsApiGatewayRestApiPolicyResourceType &&
			res.ResourceId() == api {
			return true
		}
	}
	return false
}
