package gosqs

import (
	"fmt"
	"net/http"
	"testing"
	"time"

	"github.com/Admiral-Piett/goaws/app/test"

	"github.com/Admiral-Piett/goaws/app/fixtures"
	"github.com/Admiral-Piett/goaws/app/interfaces"
	"github.com/Admiral-Piett/goaws/app/models"
	"github.com/Admiral-Piett/goaws/app/utils"
	"github.com/mitchellh/copystructure"
	"github.com/stretchr/testify/assert"
)

func TestCreateQueueV1_success(t *testing.T) {
	models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
	defer func() {
		models.ResetApp()
		utils.REQUEST_TRANSFORMER = utils.TransformRequest
	}()

	utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
		v := resultingStruct.(*models.CreateQueueRequest)
		*v = fixtures.CreateQueueRequest
		return true
	}

	_, r := test.GenerateRequestInfo("POST", "/", nil, true)
	code, response := CreateQueueV1(r)

	assert.Equal(t, http.StatusOK, code)
	assert.Equal(t, fixtures.CreateQueueResponse, response)

	actualQueue := models.SyncQueues.Queues[fixtures.QueueName]
	assert.Equal(t, fixtures.FullyPopulatedQueue, actualQueue)
}

func TestCreateQueueV1_success_with_redrive_policy(t *testing.T) {
	models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
	defer func() {
		models.ResetApp()
		utils.REQUEST_TRANSFORMER = utils.TransformRequest
	}()

	utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
		dupe, _ := copystructure.Copy(fixtures.CreateQueueRequest)
		c, _ := dupe.(models.CreateQueueRequest)
		c.Attributes.RedrivePolicy = models.RedrivePolicy{
			MaxReceiveCount:     100,
			DeadLetterTargetArn: fmt.Sprintf("arn:aws:sqs:us-east-1:100010001000:%s", fixtures.DeadLetterQueueName),
		}

		v := resultingStruct.(*models.CreateQueueRequest)
		*v = c
		return true
	}

	dlq := &models.Queue{
		Name: fixtures.DeadLetterQueueName,
	}
	models.SyncQueues.Queues[fixtures.DeadLetterQueueName] = dlq

	expectedQueue := &models.Queue{
		Name: fixtures.QueueName,
		URL: fmt.Sprintf("http://%s.%s:%s/%s/%s",
			fixtures.LOCAL_ENVIRONMENT.Region,
			fixtures.LOCAL_ENVIRONMENT.Host,
			fixtures.LOCAL_ENVIRONMENT.Port,
			fixtures.LOCAL_ENVIRONMENT.AccountID,
			fixtures.QueueName,
		),
		Arn: fmt.Sprintf("arn:aws:sqs:%s:%s:%s",
			fixtures.LOCAL_ENVIRONMENT.Region,
			fixtures.LOCAL_ENVIRONMENT.AccountID,
			fixtures.QueueName,
		),
		VisibilityTimeout:             5,
		ReceiveMessageWaitTimeSeconds: 4,
		DelaySeconds:                  1,
		MaximumMessageSize:            2,
		MessageRetentionPeriod:        3,
		DeadLetterQueue:               dlq,
		MaxReceiveCount:               100,
		Duplicates:                    make(map[string]time.Time),
	}

	_, r := test.GenerateRequestInfo("POST", "/", nil, true)
	code, response := CreateQueueV1(r)

	assert.Equal(t, http.StatusOK, code)
	assert.Equal(t, fixtures.CreateQueueResponse, response)

	actualQueue := models.SyncQueues.Queues[fixtures.QueueName]
	assert.Equal(t, expectedQueue, actualQueue)
}

func TestCreateQueueV1_success_with_existing_queue(t *testing.T) {
	models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
	defer func() {
		models.ResetApp()
		utils.REQUEST_TRANSFORMER = utils.TransformRequest
	}()

	utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
		v := resultingStruct.(*models.CreateQueueRequest)
		*v = fixtures.CreateQueueRequest
		return true
	}

	q := &models.Queue{
		Name: fixtures.QueueName,
	}
	models.SyncQueues.Queues[fixtures.QueueName] = q

	_, r := test.GenerateRequestInfo("POST", "/", nil, true)
	code, response := CreateQueueV1(r)

	assert.Equal(t, http.StatusOK, code)
	assert.Equal(t, fixtures.CreateQueueResponse, response)

	actualQueue := models.SyncQueues.Queues[fixtures.QueueName]
	assert.Equal(t, q, actualQueue)
}

func TestCreateQueueV1_success_with_no_request_attributes_falls_back_to_default(t *testing.T) {
	models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
	defer func() {
		models.ResetApp()
		utils.REQUEST_TRANSFORMER = utils.TransformRequest
	}()

	utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
		dupe, _ := copystructure.Copy(fixtures.CreateQueueRequest)
		c, _ := dupe.(models.CreateQueueRequest)
		c.Attributes = models.QueueAttributes{}

		v := resultingStruct.(*models.CreateQueueRequest)
		*v = c
		return true
	}

	expectedQueue := &models.Queue{
		Name: fixtures.QueueName,
		URL: fmt.Sprintf("http://%s.%s:%s/%s/%s",
			fixtures.LOCAL_ENVIRONMENT.Region,
			fixtures.LOCAL_ENVIRONMENT.Host,
			fixtures.LOCAL_ENVIRONMENT.Port,
			fixtures.LOCAL_ENVIRONMENT.AccountID,
			fixtures.QueueName,
		),
		Arn: fmt.Sprintf("arn:aws:sqs:%s:%s:%s",
			fixtures.LOCAL_ENVIRONMENT.Region,
			fixtures.LOCAL_ENVIRONMENT.AccountID,
			fixtures.QueueName,
		),
		VisibilityTimeout:             0,
		ReceiveMessageWaitTimeSeconds: 0,
		DelaySeconds:                  0,
		MaximumMessageSize:            0,
		MessageRetentionPeriod:        0,
		Duplicates:                    make(map[string]time.Time),
	}

	_, r := test.GenerateRequestInfo("POST", "/", nil, true)
	code, response := CreateQueueV1(r)

	assert.Equal(t, http.StatusOK, code)
	assert.Equal(t, fixtures.CreateQueueResponse, response)

	actualQueue := models.SyncQueues.Queues[fixtures.QueueName]
	assert.Equal(t, expectedQueue, actualQueue)
}

func TestCreateQueueV1_success_no_configured_region_for_queue_url(t *testing.T) {
	models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
	models.CurrentEnvironment.Region = ""
	defer func() {
		models.ResetApp()
		utils.REQUEST_TRANSFORMER = utils.TransformRequest
	}()

	utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
		dupe, _ := copystructure.Copy(fixtures.CreateQueueRequest)
		c, _ := dupe.(models.CreateQueueRequest)
		c.Attributes = models.QueueAttributes{}

		v := resultingStruct.(*models.CreateQueueRequest)
		*v = c
		return true
	}

	_, r := test.GenerateRequestInfo("POST", "/", nil, true)
	code, _ := CreateQueueV1(r)

	assert.Equal(t, http.StatusOK, code)

	actualQueue := models.SyncQueues.Queues[fixtures.QueueName]
	assert.Equal(t,
		fmt.Sprintf("http://%s:%s/%s/%s",
			fixtures.LOCAL_ENVIRONMENT.Host,
			fixtures.LOCAL_ENVIRONMENT.Port,
			fixtures.LOCAL_ENVIRONMENT.AccountID,
			fixtures.QueueName,
		),
		actualQueue.URL,
	)
}

func TestCreateQueueV1_request_transformer_error(t *testing.T) {
	models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
	defer func() {
		models.ResetApp()
		utils.REQUEST_TRANSFORMER = utils.TransformRequest
	}()

	utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
		return false
	}

	_, r := test.GenerateRequestInfo("POST", "/", nil, true)
	code, _ := CreateQueueV1(r)

	assert.Equal(t, http.StatusBadRequest, code)
}

func TestCreateQueueV1_invalid_dead_letter_queue_error(t *testing.T) {
	models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
	defer func() {
		models.ResetApp()
		utils.REQUEST_TRANSFORMER = utils.TransformRequest
	}()

	utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
		dupe, _ := copystructure.Copy(fixtures.CreateQueueRequest)
		c, _ := dupe.(models.CreateQueueRequest)
		c.Attributes.RedrivePolicy = models.RedrivePolicy{
			MaxReceiveCount:     100,
			DeadLetterTargetArn: fmt.Sprintf("arn:aws:sqs:us-east-1:100010001000:%s", "garbage"),
		}

		v := resultingStruct.(*models.CreateQueueRequest)
		*v = c
		return true
	}

	_, r := test.GenerateRequestInfo("POST", "/", nil, true)
	code, _ := CreateQueueV1(r)

	assert.Equal(t, http.StatusBadRequest, code)
}
