package sql_test

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

	"github.com/stretchr/testify/suite"
)

type CategoryRepositoryTestSuite struct {
	testutil.IntegrationTestSuite
	CategoryRepo domain.CategoryRepository
}

func (s *CategoryRepositoryTestSuite) SetupSuite() {
	s.IntegrationTestSuite.SetupSuite(testutil.DefaultTestConfig())
	cfg, err := config.LoadConfig()
	s.Require().NoError(err)
	s.CategoryRepo = sql.NewCategoryRepository(s.DB, cfg)
}

func (s *CategoryRepositoryTestSuite) SetupTest() {
	s.IntegrationTestSuite.SetupTest()
	s.DB.Exec("DELETE FROM work_categories")
	s.DB.Exec("DELETE FROM categories")
	s.DB.Exec("DELETE FROM works")
}

func TestCategoryRepository(t *testing.T) {
	suite.Run(t, new(CategoryRepositoryTestSuite))
}

func (s *CategoryRepositoryTestSuite) createCategory(name string, parentID *uint) *domain.Category {
	category := &domain.Category{Name: name, ParentID: parentID}
	err := s.CategoryRepo.Create(context.Background(), category)
	s.Require().NoError(err)
	s.Require().NotZero(category.ID)
	return category
}

func (s *CategoryRepositoryTestSuite) TestFindByName() {
	s.Run("should find a category by its name", func() {
		// Arrange
		s.createCategory("Fiction", nil)

		// Act
		found, err := s.CategoryRepo.FindByName(context.Background(), "Fiction")

		// Assert
		s.Require().NoError(err)
		s.Require().NotNil(found)
		s.Equal("Fiction", found.Name)
	})

	s.Run("should return error if not found", func() {
		_, err := s.CategoryRepo.FindByName(context.Background(), "NonExistent")
		s.Require().Error(err)
	})
}

func (s *CategoryRepositoryTestSuite) TestListByWorkID() {
	s.Run("should return all categories for a given work", func() {
		// Arrange
		work := s.CreateTestWork(s.AdminCtx, "Test Work", "en", "Test content")
		cat1 := s.createCategory("Science Fiction", nil)
		cat2 := s.createCategory("Cyberpunk", &cat1.ID)

		err := s.DB.Model(&work).Association("Categories").Append([]*domain.Category{cat1, cat2})
		s.Require().NoError(err)

		// Act
		categories, err := s.CategoryRepo.ListByWorkID(context.Background(), work.ID)

		// Assert
		s.Require().NoError(err)
		s.Len(categories, 2)
		s.ElementsMatch([]string{"Science Fiction", "Cyberpunk"}, []string{categories[0].Name, categories[1].Name})
	})
}

func (s *CategoryRepositoryTestSuite) TestListByParentID() {
	s.Run("should return top-level categories when parent ID is nil", func() {
		// Arrange
		s.createCategory("Root 1", nil)
		s.createCategory("Root 2", nil)
		child := s.createCategory("Child 1", &[]uint{1}[0]) // Create a child to ensure it's not returned

		// Act
		categories, err := s.CategoryRepo.ListByParentID(context.Background(), nil)

		// Assert
		s.Require().NoError(err)
		s.Len(categories, 2)
		s.NotContains(categories, child)
	})

	s.Run("should return child categories for a given parent ID", func() {
		// Arrange
		parent := s.createCategory("Parent", nil)
		s.createCategory("Sub-Child 1", &parent.ID)
		s.createCategory("Sub-Child 2", &parent.ID)
		s.createCategory("Another Parent", nil)

		// Act
		categories, err := s.CategoryRepo.ListByParentID(context.Background(), &parent.ID)

		// Assert
		s.Require().NoError(err)
		s.Len(categories, 2)
		for _, cat := range categories {
			s.Equal(parent.ID, *cat.ParentID)
		}
	})
}