package redriver

import (
	"encoding/base64"
	"errors"
	"fmt"
	"net/url"
	"strconv"
	"strings"

	"github.com/aws/aws-sdk-go-v2/aws/arn"
	"github.com/aws/aws-sdk-go-v2/service/sqs/types"
)

func ARNFromURL(qURL string) (arn.ARN, error) {
	a := arn.ARN{}
	u, err := url.Parse(qURL)
	if err != nil {
		return a, fmt.Errorf("queue URL %q is not a valid URL: %w", qURL, err)
	}
	path := u.EscapedPath()
	pathParts := strings.Split(strings.Trim(path, "/"), "/")
	if len(pathParts) != 2 {
		return a, fmt.Errorf("queue path %q does not have exactly 2 parts", path)
	}
	hostParts := strings.Split(u.Host, ".")
	if len(hostParts) != 4 { // <service>.<region>.amazonws.com
		return a, fmt.Errorf("queue host %q does not have exactly 4 parts", u.Host)
	}

	a = arn.ARN{
		Partition: "aws",
		Service:   hostParts[0],
		Region:    hostParts[1],
		AccountID: pathParts[0],
		Resource:  pathParts[1],
	}
	return a, nil
}

func NameFromURL(qURL string) (name string, err error) {
	u, err := url.Parse(qURL)
	if err != nil {
		return "", fmt.Errorf("queue URL %q is not a valid URL: %w", qURL, err)
	}
	path := u.EscapedPath()
	parts := strings.Split(strings.Trim(path, "/"), "/")
	if len(parts) != 2 {
		return "", fmt.Errorf("queue path %q does not have exactly 2 parts", path)
	}
	return parts[1], nil
}

func NameFromARN(qARN arn.ARN) string {
	return qARN.Resource
}

func NameFromARNString(qARN string) (name string, err error) {
	a, err := arn.Parse(qARN)
	if err != nil {
		return "", fmt.Errorf("queue \"ARN\" %q is not an valid ARN", qARN)
	}
	return NameFromARN(a), nil
}

func URLFromARNString(qARN string) (name string, err error) {
	a, err := arn.Parse(qARN)
	if err != nil {
		return "", fmt.Errorf("queue \"ARN\" %q is not an valid ARN", qARN)
	}
	return URLFromARN(a)
}

func URLFromARN(qARN arn.ARN) (name string, err error) {
	path, err := url.JoinPath("/", qARN.AccountID, qARN.Resource)
	if err != nil {
		return "", fmt.Errorf("incorrect queue ARN: %w", err)
	}
	u := url.URL{
		Scheme: "https",
		Host:   fmt.Sprintf("%s.%s.%s", qARN.Service, qARN.Region, "amazonws.com"),
		Path:   path,
	}
	return u.String(), nil
}

func JSONableFromMessageAttributeValues(m map[string]types.MessageAttributeValue) (map[string]any, error) {
	j := make(map[string]any)
	for name, attr := range m {
		if attr.DataType == nil {
			return nil, errors.New("empty DataType on message attribute value")
		}
		switch t := *attr.DataType; t {
		case "String":
			if attr.StringValue == nil {
				return nil, fmt.Errorf("nil string value on string field %q", name)
			}
			j[name] = *attr.StringValue
		case "Number":
			if attr.StringValue == nil {
				return nil, fmt.Errorf("nil string value on number field %q", name)
			}
			n, err := strconv.Atoi(*attr.StringValue)
			if err != nil {
				return nil, fmt.Errorf("invalid numeric string on number field %q", name)
			}
			j[name] = n
		case "Binary":
			if attr.BinaryValue == nil {
				j[name] = nil
				continue
			}
			in := attr.BinaryValue
			out := make([]byte, base64.StdEncoding.DecodedLen(len(in)))
			_, err := base64.StdEncoding.Decode(out, in)
			if err != nil {
				return nil, fmt.Errorf("failed decoding binary field %q: %w", name, err)
			}
			j[name] = out
		default:
			return nil, fmt.Errorf("unimplemented DataType %q", t)
		}
	}
	return j, nil
}