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 ProgramTestSuite struct {
	suite.Suite
	db         *gorm.DB
	repository repositories.ProgramRepository
	service    services.ProgramService
	controller controllers.ProgramController
}

func (suite *ProgramTestSuite) 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.Program{})

	repository := repositories.NewProgramRepository(db)
	service := services.NewProgramService(repository)
	controller := controllers.NewProgramController(service)

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

	suite.CreateSampleData()
}

func (suite *ProgramTestSuite) CreateSampleData() {
	var programs []models.Program
	for i := 1; i < 101; i++ {
		t := []string{"tag" + strconv.Itoa(i), "taga", "tagb"}
		var j datatypes.JSON
		j, _ = json.Marshal(t)
		p := models.Program{
			ID:        strconv.Itoa(i) + "_id",
			Subject:   strconv.Itoa(i) + " subject",
			Content:   strconv.Itoa(i) + " content",
			Tag:       j,
			Tags:      j.String(),
			Status:    "on",
			PublishAt: time.Now(),
			UpdatedAt: time.Now(),
			CreatedAt: time.Now(),
		}
		programs = append(programs, p)
	}

	for _, program := range programs {
		suite.db.Create(&program)
	}
}

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

func (suite *ProgramTestSuite) SetupTest() {

}

func (suite *ProgramTestSuite) TearDownTest() {

}

func TestProgramSuite(t *testing.T) {
	suite.Run(t, new(ProgramTestSuite))
}

// 목록 테스트 : 1page, 10개
func (suite *ProgramTestSuite) TestListProgramSuccess() {
	programs, err := suite.repository.List("", "", 1, 10)
	assert.NoError(suite.T(), err)
	assert.NotNil(suite.T(), programs)
	assert.Equal(suite.T(), 10, len(*programs))
	program0 := (*programs)[0]
	assert.Equal(suite.T(), "100_id", program0.ID)

	program9 := (*programs)[9]
	assert.Equal(suite.T(), "91_id", program9.ID)
}

// 목록 테스트 : tag 검색
func (suite *ProgramTestSuite) TestListProgramTagSearchSuccess() {
	programs, err := suite.repository.List("", "tag9", 1, 10)
	assert.NoError(suite.T(), err)
	assert.NotNil(suite.T(), programs)
	assert.Equal(suite.T(), 10, len(*programs))
}

// Total 테스트
func (suite *ProgramTestSuite) TestTotalSuccess() {
	total, err := suite.repository.Total("", "")
	assert.NoError(suite.T(), err)
	assert.Equal(suite.T(), int64(100), total)
}

// Total 테스트 : tag 검색
func (suite *ProgramTestSuite) TestTotalTagSuccess() {
	total, err := suite.repository.Total("", "tag2")
	assert.NoError(suite.T(), err)
	assert.Equal(suite.T(), int64(11), total)
}

// 수정 테스트
func (suite *ProgramTestSuite) TestUpdateProgramSuccess() {
	t := []string{"TAG"}
	var j datatypes.JSON
	j, _ = json.Marshal(t)
	p := &models.Program{
		ID:        uuid.NewString(),
		Subject:   "My Subject",
		Content:   "My Content",
		Tag:       j,
		Tags:      j.String(),
		Status:    "on",
		PublishAt: time.Now(),
		UpdatedAt: time.Now(),
		CreatedAt: time.Now(),
	}

	program, err := suite.repository.Create(p)
	assert.NoError(suite.T(), err)

	program.Subject = "My Subject Updated"
	program.Content = "My Content Updated"
	program.Tag = j
	program.Tags = j.String()
	program.Status = "draft"

	updatedProgram, err := suite.repository.Update(program)
	assert.NoError(suite.T(), err)
	assert.Equal(suite.T(), program.Subject, updatedProgram.Subject)
	assert.Equal(suite.T(), program.Content, updatedProgram.Content)
	assert.Equal(suite.T(), program.Tag, updatedProgram.Tag)
	assert.Equal(suite.T(), program.Status, updatedProgram.Status)
}

// 삭제 테스트
func (suite *ProgramTestSuite) TestDeleteProgramSuccess() {
	t := []string{"TAG"}
	var j datatypes.JSON
	j, _ = json.Marshal(t)
	p := &models.Program{
		ID:        uuid.NewString(),
		Subject:   "My Subject",
		Content:   "My Content",
		Tag:       j,
		Tags:      j.String(),
		Status:    "on",
		PublishAt: time.Now(),
		UpdatedAt: time.Now(),
		CreatedAt: time.Now(),
	}
	_, err := suite.repository.Create(p)
	assert.NoError(suite.T(), err)
	err = suite.repository.Delete(p.ID)
	assert.NoError(suite.T(), err)
	_, err = suite.repository.Find(p.ID)
	assert.Error(suite.T(), err)
}
