package learsteam_quiz_test

import (
	"encoding/json"
	"learnsteam/learsteam-quiz-api/internal/controllers"
	"learnsteam/learsteam-quiz-api/internal/models"
	"learnsteam/learsteam-quiz-api/internal/repositories"
	"learnsteam/learsteam-quiz-api/internal/services"
	"os"
	"strconv"
	"testing"
	"time"

	"github.com/google/uuid"
	"github.com/stretchr/testify/suite"
	"github.com/tj/assert"
	"gorm.io/datatypes"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/schema"
)

type QuizTestSuite struct {
	suite.Suite
	db         *gorm.DB
	repository repositories.QuizRepository
	service    services.QuizService
	controller controllers.QuizController
}

func (suite *QuizTestSuite) SetupSuite() {
	// err := os.Remove("test.db")
	// if err != nil {
	// 	suite.Fail("Failed to remove the test database file")
	// }

	gorm_config := gorm.Config{NamingStrategy: schema.NamingStrategy{SingularTable: true}}
	db, _ := gorm.Open(sqlite.Open("test.db"), &gorm_config)
	db.AutoMigrate(&models.Quiz{})

	repository := repositories.NewQuizRepository(db)
	service := services.NewQuizService(repository)
	controller := controllers.NewQuizController(service)

	suite.db = db
	suite.service = service
	suite.repository = repository
	suite.controller = controller

	suite.CreateSampleData()
}

func (suite *QuizTestSuite) CreateSampleData() {
	var quizzes []models.Quiz
	program_id := "1_id"

	c := []string{"1", "2", "3", "4"}
	var j datatypes.JSON
	j, _ = json.Marshal(c)

	for i := 1; i < 6; i++ {
		q := models.Quiz{
			ID:        strconv.Itoa(i),
			ProgramID: program_id,
			Sequence:  i,
			QuizType:  "multiple",
			Question:  strconv.Itoa(i) + " question",
			Choice:    j,
			Answer:    j,
			Hint:      "힌트입니다.",
			Comment:   "설명입니다.",
			UpdatedAt: time.Now(),
			CreatedAt: time.Now(),
		}
		quizzes = append(quizzes, q)
	}
	for _, quiz := range quizzes {
		suite.db.Create(&quiz)
	}
}

func (suite *QuizTestSuite) TearDownSuite() {
	// DB 삭제
	suite.db.Migrator().DropTable(&models.Quiz{})
	err := os.Remove("test.db")
	if err != nil {
		suite.Fail("Failed to remove the test database file")
	}
}

func (suite *QuizTestSuite) SetupTest() {
	suite.CreateSampleData()
}

func (suite *QuizTestSuite) TearDownTest() {

}

func TestQuizSuite(t *testing.T) {
	suite.Run(t, new(QuizTestSuite))
}

// 목록 테스트
func (suite *QuizTestSuite) TestListQuizSuccess() {
	quizzes, err := suite.repository.List("id_1", 1, 10)
	assert.NoError(suite.T(), err)
	assert.NotNil(suite.T(), quizzes)
	assert.Equal(suite.T(), 5, len(*quizzes))
}

// Find 테스트
func (suite *QuizTestSuite) TestFindQuizSuccess() {
	quiz, err := suite.repository.Find("1")
	assert.NoError(suite.T(), err)
	assert.Equal(suite.T(), "1", quiz.ID)
}

// Find 테스트
func (suite *QuizTestSuite) TestFindQuizFail() {
	_, err := suite.repository.Find("x")
	assert.Error(suite.T(), err)
}

// 수정 테스트
func (suite *QuizTestSuite) TestUpdateQuizFail() {
	c := []string{"1", "2", "3", "4"}
	var j datatypes.JSON
	j, _ = json.Marshal(c)
	q := &models.Quiz{
		ID:        uuid.NewString(),
		ProgramID: uuid.NewString(),
		Sequence:  1,
		QuizType:  "multiple",
		Question:  "question",
		Choice:    j,
		Answer:    j,
		Hint:      "힌트입니다.",
		Comment:   "설명입니다.",
		UpdatedAt: time.Now(),
		CreatedAt: time.Now(),
	}

	quiz, err := suite.repository.Create(q)
	assert.NoError(suite.T(), err)

	quiz.QuizType = "check"
	quiz.Choice = j
	quiz.Answer = j
	quiz.Hint = "힌트입니다!"

	updatedQuiz, err := suite.repository.Update(quiz)
	assert.NoError(suite.T(), err)

	assert.Equal(suite.T(), updatedQuiz.QuizType, quiz.QuizType)
	assert.Equal(suite.T(), updatedQuiz.Choice, quiz.Choice)
	assert.Equal(suite.T(), updatedQuiz.Answer, quiz.Answer)
}

// 삭제 테스트
func (suite *QuizTestSuite) TestDeleteQuizSuccess() {
	c := []string{"1", "2", "3", "4"}
	var j datatypes.JSON
	j, _ = json.Marshal(c)
	q := &models.Quiz{
		ID:        uuid.NewString(),
		ProgramID: uuid.NewString(),
		Sequence:  1,
		QuizType:  "multiple",
		Question:  "question",
		Choice:    j,
		Answer:    j,
		Hint:      "힌트입니다.",
		Comment:   "설명입니다.",
		UpdatedAt: time.Now(),
		CreatedAt: time.Now(),
	}
	_, err := suite.repository.Create(q)
	assert.NoError(suite.T(), err)
	err = suite.repository.Delete(q.ID)
	assert.NoError(suite.T(), err)
	_, err = suite.repository.Find(q.ID)
	assert.Error(suite.T(), err)
}
