package policy

import (
	"encoding/json"
	"fmt"
	"net/url"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/iam"
)

// Get retrieves a role
// No authorization necessary to view a role
func (pg *IAMPolicyGenerator) Get(roleName string) (role *IAMRole, err error) {
	if roleName == "" {
		err = InvalidInputError{prepend: "policy.Get", msg: "roleName cannot be empty"}
		return
	}
	getRoleOutput, err := pg.IAM.GetRole(&iam.GetRoleInput{RoleName: aws.String(roleName)})
	if err != nil {
		return
	}

	assumeRolePolicyDoc, err := url.QueryUnescape(aws.StringValue(getRoleOutput.Role.AssumeRolePolicyDocument))
	if err != nil {
		return
	}

	var assumeRolePolicy IAMAssumeRolePolicy
	err = json.Unmarshal([]byte(assumeRolePolicyDoc), &assumeRolePolicy)
	if err != nil {
		return
	}

	_, doc, err := pg.GetAttachedRolePolicy(roleName)
	if err != nil {
		return
	}

	auxArn, _, err := pg.GetAttachedRoleAuxPolicy(roleName)
	if err != nil {
		return
	}

	var writeAccess bool
	switch auxArn {
	case pg.RWAuxPolicyArn:
		writeAccess = true
	case pg.AuxPolicyArn:
		writeAccess = false
	default:
		writeAccess = false
	}

	var allowedArns []string
	if auxArn != "" {
		var t interface{}
		t = assumeRolePolicy.Statement[0].Principal["AWS"]
		switch t := t.(type) {
		default:
			err = fmt.Errorf("unexpected type %T when parsing assume role policy", t)
			return
		case string:
			allowedArns = []string{t}
		case []interface{}:
			for _, v := range t {
				if v, ok := v.(string); ok {
					allowedArns = append(allowedArns, v)
				} else {
					err = fmt.Errorf("unexpected type %T when parsing assume role policy", t)
					return
				}
			}
		}
	}
	owners, err := pg.GetRoleOwners(roleName)
	if err != nil {
		return
	}

	if owners == nil {
		owners = &Owners{
			InvalidState: true,
		}
	}

	role = &IAMRole{
		RoleName:         roleName,
		RoleArn:          aws.StringValue(getRoleOutput.Role.Arn),
		Policy:           doc,
		AssumeRolePolicy: pg.ExternalAssumeRolePolicyDocument(aws.StringValue(getRoleOutput.Role.Arn)),
		AuxPolicyArn:     auxArn,
		AllowedArns:      allowedArns,
		WriteAccess:      writeAccess,
		Owners:           owners,
	}

	// Check existence of group. If exists, return group name
	groupName := roleName

	if pg.CheckGroupExistence(groupName) {
		role.Group = groupName
	}
	return
}

// CheckRoleExistence will attempt to get a role. Returns true if getting the group succeeds
// ASSUMES:
//  -any failure from GetRole (even network failure) will return false
// Also, will fail if the group exists but the IAM policy for creds used to make the call doesn't
// 		have perms to access it
func (pg *IAMPolicyGenerator) CheckRoleExistence(roleName string) bool {
	_, err := pg.IAM.GetRole(&iam.GetRoleInput{RoleName: aws.String(roleName)})
	if err != nil {
		return false
	}
	return true
}
