//go:build end2end

package end2end

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

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

type BenefactorKindBoxReqsTestSuite struct {
	suite.Suite
	benefactorPhone string
	benefactorID    uint
	kindBoxReqID    uint
	getAllExpected  map[string]interface{}
	createData      benefactorkindboxreqparam.KindBoxReqAddRequest
	updateData      benefactorkindboxreqparam.KindBoxReqUpdateRequest
}

func TestBenefactorKindBoxReqsTestSuite(t *testing.T) {
	suite.Run(t, new(BenefactorKindBoxReqsTestSuite))
}

func (suite *BenefactorKindBoxReqsTestSuite) SetupTest() {
	teardown := setup.SeedMariaDB(testContainer.GetMariaDBConfig())
	suite.T().Cleanup(teardown)

	suite.benefactorPhone = "09384664404"
	suite.benefactorID = uint(1)
	suite.kindBoxReqID = uint(1)

	suite.getAllExpected = map[string]interface{}{
		"count": 5,
	}

	suite.createData = benefactorkindboxreqparam.KindBoxReqAddRequest{
		KindBoxType:        entity.KindBoxCylindrical,
		DeliverAddressID:   uint(1),
		DeliverReferDate:   time.Now().AddDate(0, 0, 7).UTC(),
		DeliverReferTimeID: uint(1),
		CountRequested:     uint(5),
	}

	suite.updateData = benefactorkindboxreqparam.KindBoxReqUpdateRequest{
		KindBoxType:        entity.KindBoxCylindrical,
		CountRequested:     uint(10),
		Description:        "updated description",
		DeliverReferTimeID: uint(1),
		DeliverReferDate:   time.Now().AddDate(0, 0, 7).UTC(),
		DeliverAddressID:   uint(1),
	}
}

func (suite *BenefactorKindBoxReqsTestSuite) TestBenefactorKindBoxReqs_GetAll_Success() {
	suite.Run("Success", func() {
		token := LoginBenefactor(suite.benefactorPhone)
		url := fmt.Sprintf("/benefactors/kindboxreqs")
		responseRecord := CreateRequest(http.MethodGet, url, token, nil)
		suite.Require().Equal(http.StatusOK, responseRecord.Code)

		var response benefactorkindboxreqparam.GetAllResponse
		err := json.NewDecoder(responseRecord.Body).Decode(&response)
		suite.Require().NoError(err, "failed to decode response body")

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

	suite.Run("Failure_Unauthorized", func() {
		url := fmt.Sprintf("/benefactors/kindboxreqs")
		responseRecord := CreateRequest(http.MethodGet, url, "invalid_token", nil)
		suite.Require().Equal(http.StatusUnauthorized, responseRecord.Code)
	})
}

func (suite *BenefactorKindBoxReqsTestSuite) TestBenefactorKindBoxReqs_Get_Success() {
	suite.Run("Success", func() {
		token := LoginBenefactor(suite.benefactorPhone)
		url := fmt.Sprintf("/benefactors/kindboxreqs/%d", suite.kindBoxReqID)
		responseRecord := CreateRequest(http.MethodGet, url, token, nil)
		suite.Require().Equal(http.StatusOK, responseRecord.Code)

		var response benefactorkindboxreqparam.KindBoxReqGetResponse
		err := json.NewDecoder(responseRecord.Body).Decode(&response)
		suite.Require().NoError(err, "failed to decode response body")

		kinBoxReq, err := services.BenefactorKindBoxReqSvc.Get(context.Background(),
			benefactorkindboxreqparam.KindBoxReqGetRequest{
				BenefactorID: suite.benefactorID,
				KindBoxReqID: suite.kindBoxReqID,
			})

		suite.Require().NoError(err, "failed to get kind box request")
		assert.Equal(suite.T(), kinBoxReq.Data.KindBoxType, response.Data.KindBoxType)
		assert.Equal(suite.T(), kinBoxReq.Data.CountRequested, response.Data.CountRequested)
		assert.Equal(suite.T(), kinBoxReq.Data.Description, response.Data.Description)
		assert.Equal(suite.T(), kinBoxReq.Data.DeliverReferTimeID, response.Data.DeliverReferTimeID)
	})

	suite.Run("Failure_NotFound", func() {
		token := LoginBenefactor(suite.benefactorPhone)
		url := fmt.Sprintf("/benefactors/kindboxreqs/%d", 9999) // Non-existent ID
		responseRecord := CreateRequest(http.MethodGet, url, token, nil)
		suite.Require().Equal(http.StatusNotFound, responseRecord.Code)
	})

	suite.Run("Failure_Unauthorized", func() {
		url := fmt.Sprintf("/benefactors/kindboxreqs/%d", suite.kindBoxReqID)
		responseRecord := CreateRequest(http.MethodGet, url, "invalid_token", nil)
		suite.Require().Equal(http.StatusUnauthorized, responseRecord.Code)
	})
}

func (suite *BenefactorKindBoxReqsTestSuite) TestBenefactorKindBoxReqs_Create_Success() {
	suite.Run("Success", func() {
		token := LoginBenefactor(suite.benefactorPhone)
		url := fmt.Sprintf("/benefactors/kindboxreqs")
		rec := CreateRequest(http.MethodPost, url, token, suite.createData)
		suite.Require().Equal(http.StatusCreated, rec.Code)

		var response benefactorkindboxreqparam.KindBoxReqAddResponse
		err := json.NewDecoder(rec.Body).Decode(&response)
		suite.Require().NoError(err, "failed to decode response body")

		assert.Equal(suite.T(), suite.createData.KindBoxType, response.Data.KindBoxType)
		assert.Equal(suite.T(), suite.createData.DeliverAddressID, response.Data.DeliverAddressID)
		assert.Equal(suite.T(), suite.createData.DeliverReferDate, response.Data.DeliverReferDate)
		assert.Equal(suite.T(), suite.createData.CountRequested, response.Data.CountRequested)
	})

	suite.Run("Failure_Unauthorized", func() {
		url := fmt.Sprintf("/benefactors/kindboxreqs")
		rec := CreateRequest(http.MethodPost, url, "invalid_token", suite.createData)
		suite.Require().Equal(http.StatusUnauthorized, rec.Code)
	})
}

func (suite *BenefactorKindBoxReqsTestSuite) TestBenefactorKindBoxReqs_Update_Success() {
	suite.Run("Success", func() {
		token := LoginBenefactor(suite.benefactorPhone)
		url := fmt.Sprintf("/benefactors/kindboxreqs/%d", suite.kindBoxReqID)
		rec := CreateRequest(http.MethodPut, url, token, suite.updateData)
		suite.Require().Equal(http.StatusNoContent, rec.Code)

		kindBoxReq, err := services.BenefactorKindBoxReqSvc.Get(context.Background(),
			benefactorkindboxreqparam.KindBoxReqGetRequest{
				BenefactorID: suite.benefactorID,
				KindBoxReqID: suite.kindBoxReqID,
			},
		)
		suite.Require().NoError(err, "failed to get kind box request")

		assert.Equal(suite.T(), suite.updateData.CountRequested, kindBoxReq.Data.CountRequested)
		assert.Equal(suite.T(), suite.updateData.Description, kindBoxReq.Data.Description)
		assert.Equal(suite.T(), suite.updateData.DeliverReferTimeID, kindBoxReq.Data.DeliverReferTimeID)
		assert.Equal(
			suite.T(),
			suite.updateData.DeliverReferDate.Format("2006-01-02"),
			kindBoxReq.Data.DeliverReferDate.Format("2006-01-02"),
		)
		assert.Equal(suite.T(), suite.updateData.DeliverAddressID, kindBoxReq.Data.DeliverAddressID)
	})

	suite.Run("Failure_Unauthorized", func() {
		url := fmt.Sprintf("/benefactors/kindboxreqs/%d", suite.kindBoxReqID)
		rec := CreateRequest(http.MethodPut, url, "invalid_token", suite.updateData)
		suite.Require().Equal(http.StatusUnauthorized, rec.Code)
	})
}

func (suite *BenefactorKindBoxReqsTestSuite) TestBenefactorKindBoxReqs_Delete_Success() {
	suite.Run("Success", func() {
		token := LoginBenefactor(suite.benefactorPhone)
		url := fmt.Sprintf("/benefactors/kindboxreqs/%d", suite.kindBoxReqID)
		rec := CreateRequest(http.MethodDelete, url, token, nil)
		suite.Require().Equal(http.StatusOK, rec.Code)

		_, err := services.BenefactorKindBoxReqSvc.Get(context.Background(),
			benefactorkindboxreqparam.KindBoxReqGetRequest{
				BenefactorID: suite.benefactorID,
				KindBoxReqID: suite.kindBoxReqID,
			},
		)
		message, code := httpmsg.Error(err)
		suite.Require().Error(err)
		assert.Equal(suite.T(), http.StatusNotFound, code)
		assert.Equal(suite.T(), errmsg.ErrorMsgNotFound, message)
	})

	suite.Run("Failure_Unauthorized", func() {
		url := fmt.Sprintf("/benefactors/kindboxreqs/%d", suite.kindBoxReqID)
		rec := CreateRequest(http.MethodDelete, url, "invalid_token", nil)
		suite.Require().Equal(http.StatusUnauthorized, rec.Code)
	})
}
