package benefactorkindboxreqvalidator_test

import (
	"context"
	"fmt"
	"testing"
	"time"

	"git.gocasts.ir/ebhomengo/niki/entity"
	addressparam "git.gocasts.ir/ebhomengo/niki/param/benefactor/address"
	param "git.gocasts.ir/ebhomengo/niki/param/benefactor/benefactore"
	benefactorkindboxreqparam "git.gocasts.ir/ebhomengo/niki/param/benefactor/kind_box_req"
	errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
	benefactorkindboxreqvalidator "git.gocasts.ir/ebhomengo/niki/validator/benefactor/kind_box_req"
	"github.com/stretchr/testify/assert"
)

const RepoErr = "repository error"

type StubService struct {
	addresses   []entity.Address
	benefactors []entity.Benefactor
	haveError   bool
}

func NewMock(haveError bool) *StubService {
	var addresses []entity.Address
	var benefactors []entity.Benefactor

	addresses = append(addresses, entity.Address{
		ID:           1,
		PostalCode:   "123456789",
		Address:      "tehran",
		Lat:          25.25,
		Lon:          25.26,
		CityID:       1,
		ProvinceID:   1,
		BenefactorID: 1,
	})
	benefactors = append(benefactors, entity.Benefactor{
		ID:          1,
		FirstName:   "mehdi",
		LastName:    "rez",
		PhoneNumber: "09191234556",
		Address:     "tehran",
		Description: "",
		Email:       "example@gmail.com",
		City:        "teran",
		Gender:      0,
		Status:      0,
		Birthdate:   time.Time{},
		Role:        1,
	})

	return &StubService{
		addresses:   addresses,
		benefactors: benefactors,
		haveError:   haveError,
	}
}

func (s StubService) BenefactorExistByID(ctx context.Context, request param.BenefactorExistByIDRequest) (param.BenefactorExistByIDResponse, error) {
	if s.haveError {
		// error response
		return param.BenefactorExistByIDResponse{Existed: false}, fmt.Errorf(RepoErr)
	}

	for _, benefactor := range s.benefactors {
		if benefactor.ID == request.ID {
			return param.BenefactorExistByIDResponse{Existed: true}, nil
		}
	}
	return param.BenefactorExistByIDResponse{Existed: false}, nil
}

func (s StubService) AddressExistByID(ctx context.Context, request addressparam.GetAddressByIDRequest) (addressparam.GetAddressByIDResponse, error) {
	if s.haveError {
		// error response
		return addressparam.GetAddressByIDResponse{Address: nil}, fmt.Errorf(RepoErr)
	}
	for _, address := range s.addresses {
		if address.ID == request.ID {
			return addressparam.GetAddressByIDResponse{Address: &address}, nil
		}
	}

	return addressparam.GetAddressByIDResponse{Address: nil}, nil
}

func TestValidateAddRequest(t *testing.T) {
	testCases := []struct {
		name    string
		params  benefactorkindboxreqparam.KindBoxReqAddRequest
		repoErr bool
		error   error
	}{
		{
			name: "ordinary add",
			params: benefactorkindboxreqparam.KindBoxReqAddRequest{
				AddressID:      1,
				BenefactorID:   1,
				TypeID:         1,
				CountRequested: 2,
				ReferDate:      time.Now(),
			},
		},
		{
			name:    "repo error",
			repoErr: true,
			error:   fmt.Errorf("benefactor_id: record not found\naddress_id: something went wrong\n"),
			params: benefactorkindboxreqparam.KindBoxReqAddRequest{
				AddressID:      1,
				BenefactorID:   1,
				TypeID:         1,
				CountRequested: 1,
				ReferDate:      time.Now(),
			},
		},
		{
			name:  "Count Requested cannot be empty",
			error: fmt.Errorf(fmt.Sprintf("count_requested: cannot be blank\n")),
			params: benefactorkindboxreqparam.KindBoxReqAddRequest{
				AddressID:      1,
				BenefactorID:   1,
				TypeID:         1,
				CountRequested: 0,
				ReferDate:      time.Now(),
			},
		},
		{
			name:  "TypeID cannot be empty",
			error: fmt.Errorf(fmt.Sprintf("type_id: cannot be blank\n")),
			params: benefactorkindboxreqparam.KindBoxReqAddRequest{
				AddressID:      1,
				BenefactorID:   1,
				TypeID:         0,
				CountRequested: 1,
				ReferDate:      time.Now(),
			},
		},
		{
			name:  "type with ID does exists",
			error: fmt.Errorf(fmt.Sprintf("type_id: %s\n", errmsg.ErrorMsgNotFound)),
			params: benefactorkindboxreqparam.KindBoxReqAddRequest{
				AddressID:      1,
				BenefactorID:   1,
				TypeID:         5,
				CountRequested: 1,
				ReferDate:      time.Now(),
			},
		},
		{
			name:  "AddressID cannot be empty",
			error: fmt.Errorf(fmt.Sprintf("address_id: cannot be blank\n")),
			params: benefactorkindboxreqparam.KindBoxReqAddRequest{
				AddressID:      0,
				TypeID:         1,
				BenefactorID:   1,
				CountRequested: 1,
				ReferDate:      time.Now(),
			},
		},
		{
			name:  "address with ID does exists",
			error: fmt.Errorf(fmt.Sprintf("address_id: %s\n", errmsg.ErrorMsgNotFound)),
			params: benefactorkindboxreqparam.KindBoxReqAddRequest{
				AddressID:      5000,
				TypeID:         1,
				CountRequested: 1,
				BenefactorID:   1,
				ReferDate:      time.Now(),
			},
		},
		{
			name:  "ReferDate should not be empty",
			error: fmt.Errorf(fmt.Sprintf("refer_date: cannot be blank\n")),
			params: benefactorkindboxreqparam.KindBoxReqAddRequest{
				AddressID:      1,
				TypeID:         1,
				BenefactorID:   1,
				CountRequested: 1,
			},
		},
		{
			name:  "This address does not belong to this benefactor",
			error: fmt.Errorf(fmt.Sprintf("address_id: %s\n", errmsg.ErrorMsgNotFound)),
			params: benefactorkindboxreqparam.KindBoxReqAddRequest{
				AddressID:      1,
				BenefactorID:   100,
				TypeID:         1,
				CountRequested: 1,
				ReferDate:      time.Now(),
			},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			// 1. setup
			repo := NewMock(tc.repoErr)
			vld := benefactorkindboxreqvalidator.New(repo, repo)

			// 2. execution
			res := vld.ValidateAddRequest(tc.params)

			// 3. assertion
			if tc.error == nil {
				assert.Nil(t, res)
				return
			}
			assert.Equal(t, tc.error.Error(), res.Error())
		})
	}
}
