package report

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/sqs"

	"github.com/tidbcloud/billing-reporter/conf"
)

const (
	separateString            = "||"
	failureMessagesBatchLimit = 10
)

var (
	StringDataType = "String"
)

func setMsg(message *SqsMessage) {
	message.Region = conf.AwsRegion
	message.Url = conf.AwsSqsUrl
}

func writeSqsMessageToFile(message SqsMessage) error {
	b, err := json.Marshal(message)
	if err != nil {
		return err
	}
	return writeToFile(string(b))
}

// writeToFile write the message to the local file. If the target file already existed, it would append the message in the file.
func writeToFile(message string) error {
	// we use separateString to split each message
	message += separateString
	if _, err := os.Stat(conf.FailureMessageFile); err != nil {
		if os.IsNotExist(err) {
			// FIXME: G306: Expect WriteFile permissions to be 0600 or less (gosec)
			return ioutil.WriteFile(conf.FailureMessageFile, []byte(message), 0644) //nolint: gosec
		}
		return err
	}

	file, err := os.OpenFile(conf.FailureMessageFile, os.O_APPEND|os.O_WRONLY, 0600)
	if err != nil {
		return err
	}
	defer file.Close()
	_, err = file.WriteString(message)
	return err
}

func buildSqsMsgAttributes(attr map[string]string) map[string]*sqs.MessageAttributeValue {
	if attr == nil {
		return nil
	}
	a := map[string]*sqs.MessageAttributeValue{}
	for k, v := range attr {
		a[k] = &sqs.MessageAttributeValue{
			StringValue: aws.String(v),
			DataType:    &StringDataType,
		}
	}
	return a
}

func isFileExisted(file string) (bool, error) {
	_, err := os.Stat(file)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func readFailureMessages(file string) ([]SqsMessage, error) {
	b, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}
	list := strings.Split(string(b), separateString)
	var sqsMsgs []SqsMessage
	for _, message := range list {
		if len(message) < 1 {
			continue
		}
		sqsMsg := &SqsMessage{}
		err = json.Unmarshal([]byte(message), sqsMsg)
		if err != nil {
			return nil, err
		}
		sqsMsgs = append(sqsMsgs, *sqsMsg)
	}
	return sqsMsgs, nil
}

func removeFailureMessageFile(file string) error {
	return os.Remove(file)
}

func buildSqsBatchRequest(sqsMsgs []SqsMessage) *sqs.SendMessageBatchInput {
	var ses []*sqs.SendMessageBatchRequestEntry
	for id, msg := range sqsMsgs {
		sid := fmt.Sprintf("%d", id)
		se := &sqs.SendMessageBatchRequestEntry{
			DelaySeconds:      aws.Int64(10),
			MessageAttributes: buildSqsMsgAttributes(msg.Attributes),
			MessageBody:       &msg.Message,
			Id:                &sid,
		}
		ses = append(ses, se)
	}
	return &sqs.SendMessageBatchInput{
		QueueUrl: &conf.AwsSqsUrl,
		Entries:  ses,
	}
}

func rewriteMessagesToFile(rewriteMsgs []SqsMessage) error {
	if rewriteMsgs == nil || len(rewriteMsgs) < 1 {
		return removeFailureMessageFile(conf.FailureMessageFile)
	}
	return writeSqsMessagesToFile(rewriteMsgs)
}

// writeSqsMessagesToFile would rewrite the whole file with given messages
func writeSqsMessagesToFile(sqsMsgs []SqsMessage) error {
	message := ""
	for _, msg := range sqsMsgs {
		b, err := json.Marshal(msg)
		if err != nil {
			return err
		}
		message = fmt.Sprintf("%s%s%s", message, separateString, string(b))
	}
	return ioutil.WriteFile(conf.FailureMessageFile, []byte(message), os.ModeAppend)
}
