package sql_test

import (
	"context"
	"testing"
	"tercul/internal/data/sql"
	"tercul/internal/domain"
	"tercul/internal/platform/config"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/stretchr/testify/suite"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

type CollectionRepositoryTestSuite struct {
	suite.Suite
	db   *gorm.DB
	mock sqlmock.Sqlmock
	repo domain.CollectionRepository
}

func (s *CollectionRepositoryTestSuite) SetupTest() {
	db, mock, err := sqlmock.New()
	s.Require().NoError(err)

	gormDB, err := gorm.Open(postgres.New(postgres.Config{Conn: db}), &gorm.Config{})
	s.Require().NoError(err)

	s.db = gormDB
	s.mock = mock
	cfg, err := config.LoadConfig()
	s.Require().NoError(err)
	s.repo = sql.NewCollectionRepository(s.db, cfg)
}

func (s *CollectionRepositoryTestSuite) TearDownTest() {
	s.Require().NoError(s.mock.ExpectationsWereMet())
}

func TestCollectionRepositoryTestSuite(t *testing.T) {
	suite.Run(t, new(CollectionRepositoryTestSuite))
}

func (s *CollectionRepositoryTestSuite) TestListByUserID() {
	userID := uint(1)
	rows := sqlmock.NewRows([]string{"id", "user_id"}).
		AddRow(1, userID).
		AddRow(2, userID)

	s.mock.ExpectQuery(`SELECT \* FROM "collections" WHERE user_id = \$1`).
		WithArgs(userID).
		WillReturnRows(rows)

	collections, err := s.repo.ListByUserID(context.Background(), userID)
	s.Require().NoError(err)
	s.Require().Len(collections, 2)
}

func (s *CollectionRepositoryTestSuite) TestAddWorkToCollection() {
	collectionID, workID := uint(1), uint(1)
	s.mock.ExpectExec(`INSERT INTO collection_works \(collection_id, work_id\) VALUES \(\$1, \$2\) ON CONFLICT DO NOTHING`).
		WithArgs(collectionID, workID).
		WillReturnResult(sqlmock.NewResult(1, 1))

	err := s.repo.AddWorkToCollection(context.Background(), collectionID, workID)
	s.Require().NoError(err)
}

func (s *CollectionRepositoryTestSuite) TestRemoveWorkFromCollection() {
	collectionID, workID := uint(1), uint(1)
	s.mock.ExpectExec(`DELETE FROM collection_works WHERE collection_id = \$1 AND work_id = \$2`).
		WithArgs(collectionID, workID).
		WillReturnResult(sqlmock.NewResult(1, 1))

	err := s.repo.RemoveWorkFromCollection(context.Background(), collectionID, workID)
	s.Require().NoError(err)
}

func (s *CollectionRepositoryTestSuite) TestListPublic() {
	rows := sqlmock.NewRows([]string{"id", "is_public"}).
		AddRow(1, true).
		AddRow(2, true)

	s.mock.ExpectQuery(`SELECT \* FROM "collections" WHERE is_public = \$1`).
		WithArgs(true).
		WillReturnRows(rows)

	collections, err := s.repo.ListPublic(context.Background())
	s.Require().NoError(err)
	s.Require().Len(collections, 2)
}

func (s *CollectionRepositoryTestSuite) TestListByWorkID() {
	workID := uint(1)
	rows := sqlmock.NewRows([]string{"id"}).
		AddRow(1).
		AddRow(2)

	s.mock.ExpectQuery(`SELECT "collections"\."id","collections"\."created_at","collections"\."updated_at","collections"\."language","collections"\."slug","collections"\."name","collections"\."description","collections"\."user_id","collections"\."is_public","collections"\."cover_image_url" FROM "collections" JOIN collection_works ON collection_works\.collection_id = collections\.id WHERE collection_works\.work_id = \$1`).
		WithArgs(workID).
		WillReturnRows(rows)

	collections, err := s.repo.ListByWorkID(context.Background(), workID)
	s.Require().NoError(err)
	s.Require().Len(collections, 2)
}