//go:build end2end

package end2end

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"testing"

	"git.gocasts.ir/ebhomengo/niki/delivery/http_server/end2end/setup"
	addressparam "git.gocasts.ir/ebhomengo/niki/param/benefactor/address"
	errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
	httpmsg "git.gocasts.ir/ebhomengo/niki/pkg/http_msg"
	"github.com/stretchr/testify/suite"
)

type BenefactorAddressTestSuit struct {
	suite.Suite
	benefactorPhone string
	benefactorID    uint
	addressID       uint
	getAllExpected  map[string]interface{}
	getExpected     addressparam.GetAddressResponse
	createData      addressparam.BenefactorAddAddressRequest
	updateData      addressparam.UpdateAddressRequest
	teardown        func()
}

func TestBenefactorAddressTestSuit(t *testing.T) {
	suite.Run(t, new(BenefactorAddressTestSuit))
}

// SetupTest runs before each test in the suite
func (suite *BenefactorAddressTestSuit) SetupTest() {
	teardown := setup.SeedMariaDB(testContainer.GetMariaDBConfig())
	suite.T().Cleanup(teardown)
	suite.benefactorPhone = "09384664404"
	suite.benefactorID = 1
	suite.addressID = 1
	suite.getAllExpected = map[string]interface{}{
		"count": 1,
	}

	suite.createData = addressparam.BenefactorAddAddressRequest{
		PostalCode: "3719655861",
		Address:    "create shiraz kaf sharo",
		Lat:        29.62949,
		Lon:        52.497287,
		CityID:     194,
		Name:       "create shiraz",
	}
	suite.updateData = addressparam.UpdateAddressRequest{
		PostalCode: "3719655861",
		Address:    "update shiraz kaf sharo",
		Lat:        29.62949,
		Lon:        52.497287,
		CityID:     194,
		Name:       "update shiraz",
	}
}

func (suite *BenefactorAddressTestSuit) TestBenefactorAddressGet() {
	token := LoginBenefactor(suite.benefactorPhone)
	url := fmt.Sprintf("/benefactors/addresses/%d", suite.addressID)

	suite.T().Run("Success", func(t *testing.T) {
		responseRecord := CreateRequest("GET", url, token, nil)
		suite.Require().Equal(http.StatusOK, responseRecord.Code)

		var response addressparam.GetAddressResponse
		err := json.NewDecoder(responseRecord.Body).Decode(&response)
		suite.Require().NoError(err, "could not decode response body")
		address, sErr := services.BenefactorAddressSvc.Get(context.Background(),
			addressparam.GetAddressRequest{
				AddressID:    suite.addressID,
				BenefactorID: suite.benefactorID,
			})

		suite.Require().NoError(sErr, "failed to get benefactor address")
		suite.Require().Equal(address.Data.PostalCode, response.Data.PostalCode)
		suite.Require().Equal(address.Data.Address, response.Data.Address)
		suite.Require().Equal(address.Data.Name, response.Data.Name)
		suite.Require().Equal(address.Data.CityID, response.Data.CityID)
	})

	suite.T().Run("Failure_Unauthorized", func(t *testing.T) {
		responseRecord := CreateRequest("GET", url, "", nil) // No token provided
		suite.Require().Equal(http.StatusUnauthorized, responseRecord.Code)
	})
}

func (suite *BenefactorAddressTestSuit) TestBenefactorAddressGetAll() {
	token := LoginBenefactor(suite.benefactorPhone)
	url := fmt.Sprintf("/benefactors/addresses")

	suite.T().Run("Success", func(t *testing.T) {
		responseRecord := CreateRequest("GET", url, token, nil)
		suite.Require().Equal(http.StatusOK, responseRecord.Code)

		var response addressparam.GetAllAddressesResponse
		err := json.NewDecoder(responseRecord.Body).Decode(&response)
		suite.Require().NoError(err, "could not decode response body")

		suite.Require().Equal(suite.getAllExpected["count"], len(response.Data))
	})

	suite.T().Run("Failure_Unauthorized", func(t *testing.T) {
		responseRecord := CreateRequest("GET", url, "", nil) // No token provided
		suite.Require().Equal(http.StatusUnauthorized, responseRecord.Code)
	})
}

func (suite *BenefactorAddressTestSuit) TestBenefactorAddressCreate() {
	token := LoginBenefactor(suite.benefactorPhone)
	url := fmt.Sprintf("/benefactors/addresses")

	suite.T().Run("Success", func(t *testing.T) {
		responseRecord := CreateRequest("POST", url, token, suite.createData)
		suite.Require().Equal(http.StatusCreated, responseRecord.Code)

		var response addressparam.BenefactorAddAddressResponse
		err := json.NewDecoder(responseRecord.Body).Decode(&response)
		suite.Require().NoError(err, "could not decode response body")

		suite.Require().Equal(suite.benefactorID, response.Data.BenefactorID)
		suite.Require().Equal(suite.createData.Address, response.Data.Address)
		suite.Require().Equal(suite.createData.PostalCode, response.Data.PostalCode)
		suite.Require().Equal(suite.createData.Name, response.Data.Name)
		suite.Require().Equal(suite.createData.CityID, response.Data.CityID)
	})

	suite.T().Run("Failure_BadRequest", func(t *testing.T) {
		responseRecord := CreateRequest("POST", url, token, nil) // No data provided
		suite.Require().Equal(http.StatusUnprocessableEntity, responseRecord.Code)
	})
}

func (suite *BenefactorAddressTestSuit) TestBenefactorAddressUpdate() {
	token := LoginBenefactor(suite.benefactorPhone)
	url := fmt.Sprintf("/benefactors/addresses/%d", suite.addressID)

	suite.T().Run("Success", func(t *testing.T) {
		responseRecord := CreateRequest("PUT", url, token, suite.updateData)
		suite.Require().Equal(http.StatusNoContent, responseRecord.Code)

		updatedAddress, sErr := services.BenefactorAddressSvc.Get(context.Background(),
			addressparam.GetAddressRequest{
				AddressID:    suite.addressID,
				BenefactorID: suite.benefactorID,
			})
		suite.Require().NoError(sErr, "failed to get benefactor address")

		suite.Require().Equal(suite.updateData.PostalCode, updatedAddress.Data.PostalCode)
		suite.Require().Equal(suite.updateData.Address, updatedAddress.Data.Address)
		suite.Require().Equal(suite.updateData.Name, updatedAddress.Data.Name)
		suite.Require().Equal(suite.updateData.CityID, updatedAddress.Data.CityID)
	})

	suite.T().Run("Failure_Unauthorized", func(t *testing.T) {
		responseRecord := CreateRequest("PUT", url, "", suite.updateData) // No token provided
		suite.Require().Equal(http.StatusUnauthorized, responseRecord.Code)
	})
}

// TestBenefactorAddressDelete tests the DELETE /address/:id endpoint
func (suite *BenefactorAddressTestSuit) TestBenefactorAddressDelete() {
	token := LoginBenefactor(suite.benefactorPhone)
	url := fmt.Sprintf("/benefactors/addresses/%d", suite.addressID)

	suite.T().Run("Success", func(t *testing.T) {
		responseRecord := CreateRequest("DELETE", url, token, nil)
		suite.Require().Equal(http.StatusNoContent, responseRecord.Code)

		_, err := services.BenefactorAddressSvc.Get(context.Background(),
			addressparam.GetAddressRequest{
				AddressID:    suite.addressID,
				BenefactorID: suite.benefactorID,
			},
		)
		message, code := httpmsg.Error(err)
		suite.Require().Error(err)

		suite.Equal(http.StatusNotFound, code)
		suite.Equal(errmsg.ErrorMsgNotFound, message)
	})

	suite.T().Run("Failure_Unauthorized", func(t *testing.T) {
		responseRecord := CreateRequest("DELETE", url, "", nil) // No token provided
		suite.Require().Equal(http.StatusUnauthorized, responseRecord.Code)
	})
}
