package pickup_point

import (
	"context"
	"errors"
	"github.com/DATA-DOG/go-sqlmock"
	"github.com/UpmeshLTD/urus-aio/infrastructure/common"
	"github.com/UpmeshLTD/urus-aio/infrastructure/db/entities"
	"github.com/UpmeshLTD/urus-aio/shipment/db/gormdb"
	"github.com/UpmeshLTD/urus-aio/shipment/server/http/dto"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"
	"gorm.io/gorm"
	"testing"

	tt "github.com/UpmeshLTD/urus-aio/infrastructure/testing"
	mocks "github.com/UpmeshLTD/urus-aio/shipment/domain/mock/pickup_point"
)

func TestPickupPointService(t *testing.T) {
	suite.Run(t, new(ServiceTestSuite))
}

type ServiceTestSuite struct {
	suite.Suite
	mock    sqlmock.Sqlmock
	ctx     context.Context
	gormDB  gormdb.GormDB
	db      *gorm.DB
	repo    *mocks.Repository
	service Service
}

func (s *ServiceTestSuite) SetupSuite() {
	var err error
	s.db, s.mock, err = tt.MockGormSqlmock()
	require.NoError(s.T(), err)

	s.ctx = context.TODO()
	s.gormDB = gormdb.Wrap(s.db)
}

func (s *ServiceTestSuite) SetupTest() {
	s.repo = new(mocks.Repository)
	s.service = NewService(s.repo)
}

func (s *ServiceTestSuite) Test_GetPickupPointByID_Success() {
	sample := NewSamplePickupPoint()
	expectedResult := &sample

	s.repo.On("GetPickupPointByID", s.ctx, expectedResult.WorkspaceID, expectedResult.ID).Return(expectedResult, nil)

	result, err := s.service.GetPickupPointByID(s.ctx, expectedResult.WorkspaceID, expectedResult.ID)
	require.NoError(s.T(), err)
	assert.Equal(s.T(), expectedResult, result, "they should be equal")
}

func (s *ServiceTestSuite) Test_GetPickupPointByID_NotFound() {
	sample := NewSamplePickupPoint()
	expectedError := gorm.ErrRecordNotFound

	s.repo.On("GetPickupPointByID", s.ctx, sample.WorkspaceID, sample.ID).Return(nil, expectedError)

	result, err := s.service.GetPickupPointByID(s.ctx, sample.WorkspaceID, sample.ID)
	require.Error(s.T(), err)
	assert.Nil(s.T(), result, "result should be nil")
	assert.Equal(s.T(), expectedError, err, "they should be equal")
}

func (s *ServiceTestSuite) Test_GetPickupPointByID_Failed() {
	sample := NewSamplePickupPoint()
	expectedError := errors.New("failed to get pickup points")

	s.repo.On("GetPickupPointByID", s.ctx, sample.WorkspaceID, sample.ID).Return(nil, expectedError)

	result, err := s.service.GetPickupPointByID(s.ctx, sample.WorkspaceID, sample.ID)
	require.Error(s.T(), err)
	assert.Nil(s.T(), result, "result should be nil")
	assert.Equal(s.T(), expectedError, err, "they should be equal")
}

func (s *ServiceTestSuite) Test_GetListOfPickupPoints_Success() {
	sample := NewSamplePickupPoint()
	expectedResult := []*entities.PickupPoint{&sample}
	expectedTotal := int64(len(expectedResult))
	args := &dto.GetPickupPointsRequest{}

	s.repo.On("GetListOfPickupPoints", s.ctx, sample.WorkspaceID, args).Return(expectedResult, expectedTotal, nil)

	result, total, err := s.service.GetListOfPickupPoints(s.ctx, sample.WorkspaceID, args)
	require.NoError(s.T(), err)
	assert.Equal(s.T(), expectedResult, result, "they should be equal")
	assert.Equal(s.T(), expectedTotal, total, "they should be equal")
}

func (s *ServiceTestSuite) Test_GetListOfPickupPoints_Empty() {
	sample := NewSamplePickupPoint()
	var expectedResult []*entities.PickupPoint
	expectedTotal := int64(0)
	args := &dto.GetPickupPointsRequest{}

	s.repo.On("GetListOfPickupPoints", s.ctx, sample.WorkspaceID, args).Return(expectedResult, expectedTotal, nil)

	result, total, err := s.service.GetListOfPickupPoints(s.ctx, sample.WorkspaceID, args)
	require.NoError(s.T(), err)
	assert.Equal(s.T(), expectedResult, result, "they should be equal")
	assert.Equal(s.T(), expectedTotal, total, "they should be equal")
}

func (s *ServiceTestSuite) Test_GetListOfPickupPoints_Limit_1() {
	sample := NewSamplePickupPoint()
	expectedResult := []*entities.PickupPoint{&sample}
	expectedTotal := int64(1)
	args := &dto.GetPickupPointsRequest{
		Pagination: common.Pagination{
			Limit: 1,
		},
	}

	s.repo.On("GetListOfPickupPoints", s.ctx, sample.WorkspaceID, args).Return(expectedResult, expectedTotal, nil)

	result, total, err := s.service.GetListOfPickupPoints(s.ctx, sample.WorkspaceID, args)
	require.NoError(s.T(), err)
	assert.Equal(s.T(), expectedResult, result, "they should be equal")
	assert.Equal(s.T(), expectedTotal, total, "they should be equal")
}

func (s *ServiceTestSuite) Test_GetListOfPickupPoints_Failed() {
	sample := NewSamplePickupPoint()
	var expectedResult []*entities.PickupPoint
	expectedTotal := int64(0)
	expectedError := errors.New("failed to get pickup points")
	args := &dto.GetPickupPointsRequest{}

	s.repo.On("GetListOfPickupPoints", s.ctx, sample.WorkspaceID, args).Return(expectedResult, expectedTotal, expectedError)

	result, total, err := s.service.GetListOfPickupPoints(s.ctx, sample.WorkspaceID, args)
	require.Error(s.T(), err)
	assert.Equal(s.T(), expectedResult, result, "they should be equal")
	assert.Equal(s.T(), expectedTotal, total, "they should be equal")
	assert.Equal(s.T(), expectedError, err, "they should be equal")
}

func (s *ServiceTestSuite) Test_CreatePickupPoint_Success() {
	sample := NewSamplePickupPoint()
	expectedResult := &sample
	newPickupPoint := &entities.PickupPoint{
		WorkspaceID: expectedResult.WorkspaceID,
		FirstName:   expectedResult.FirstName,
		LastName:    expectedResult.LastName,
	}

	s.repo.On("CreatePickupPoint", s.ctx, newPickupPoint).Return(expectedResult, nil)

	result, err := s.service.CreatePickupPoint(s.ctx, newPickupPoint)
	require.NoError(s.T(), err)
	assert.Equal(s.T(), expectedResult, result, "they should be equal")
}

func (s *ServiceTestSuite) Test_CreatePickupPoint_Failed() {
	sample := NewSamplePickupPoint()
	newPickupPoint := &entities.PickupPoint{
		WorkspaceID: sample.WorkspaceID,
		FirstName:   sample.FirstName,
		LastName:    sample.LastName,
	}
	expectedError := gorm.ErrInvalidData

	s.repo.On("CreatePickupPoint", s.ctx, newPickupPoint).Return(nil, expectedError)

	result, err := s.service.CreatePickupPoint(s.ctx, newPickupPoint)
	require.Error(s.T(), err)
	assert.Nil(s.T(), result, "result should be nil")
	assert.Equal(s.T(), expectedError, err, "they should be equal")
}

func (s *ServiceTestSuite) Test_UpdatePickupPoint_Success() {
	sample := NewSamplePickupPoint()
	updatePickupPoint := &entities.PickupPoint{
		WorkspaceID: sample.WorkspaceID,
		FirstName:   sample.FirstName,
		LastName:    sample.LastName,
	}

	s.repo.On("UpdatePickupPoint", s.ctx, sample.WorkspaceID, sample.ID, updatePickupPoint).Return(nil)

	err := s.service.UpdatePickupPoint(s.ctx, sample.WorkspaceID, sample.ID, updatePickupPoint)
	require.NoError(s.T(), err)
}

func (s *ServiceTestSuite) Test_UpdatePickupPoint_Failed() {
	sample := NewSamplePickupPoint()
	updatePickupPoint := &entities.PickupPoint{
		WorkspaceID: sample.WorkspaceID,
		FirstName:   sample.FirstName,
		LastName:    sample.LastName,
	}
	expectedError := errors.New("failed to update")

	s.repo.On("UpdatePickupPoint", s.ctx, sample.WorkspaceID, sample.ID, updatePickupPoint).Return(expectedError)

	err := s.service.UpdatePickupPoint(s.ctx, sample.WorkspaceID, sample.ID, updatePickupPoint)
	require.Error(s.T(), err)
	assert.Equal(s.T(), expectedError, err, "they should be equal")
}

func (s *ServiceTestSuite) Test_DeletePickupPoint_Success() {
	sample := NewSamplePickupPoint()

	s.repo.On("DeletePickupPoint", s.ctx, sample.WorkspaceID, sample.ID).Return(nil)

	err := s.service.DeletePickupPoint(s.ctx, sample.WorkspaceID, sample.ID)
	require.NoError(s.T(), err)
}

func (s *ServiceTestSuite) Test_DeletePickupPoint_Failed() {
	sample := NewSamplePickupPoint()
	expectedError := errors.New("failed to update")

	s.repo.On("DeletePickupPoint", s.ctx, sample.WorkspaceID, sample.ID).Return(expectedError)

	err := s.service.DeletePickupPoint(s.ctx, sample.WorkspaceID, sample.ID)
	require.Error(s.T(), err)
	assert.Equal(s.T(), expectedError, err, "they should be equal")
}
