package project

import (
	"context"
	"log"
	"os"
	"testing"

	"github.com/smartystreets/goconvey/convey"
	"github.com/tidbcloud/account/internel/dao/entity"
	"github.com/tidbcloud/account/pkg/docker_db"
	"gorm.io/gorm"
)

var db *gorm.DB

func init() {
	var err error
	db, err = docker_db.TryInitCentralDB(true)
	if err != nil {
		log.Printf("Failed to init docker db: %s", err)
		os.Exit(-1)
		return
	}
}

func deleteProjectByID(id uint64) {
	log.Printf("delete project %d: %s", id, db.Where("id = ?", id).Delete(&entity.Project{}).Error)
}

func TestProjectDao_GetProjectByID(t *testing.T) {
	d := &ProjectDao{}
	ctx := context.TODO()

	convey.Convey("TestGetProjectByID", t, func() {
		convey.Convey("project not exists", func() {
			project, err := d.GetProjectByID(ctx, db, 10086)
			convey.So(err, convey.ShouldEqual, gorm.ErrRecordNotFound)
			convey.So(project, convey.ShouldBeNil)
		})

		convey.Convey("project exists", func() {
			project := entity.Project{
				TenantID:       10010,
				Name:           "test",
				Status:         "active",
				DeletedTime:    nil,
				IsCrossAccount: false,
			}
			err := db.Create(&project).Error
			convey.So(err, convey.ShouldBeNil)
			defer deleteProjectByID(project.ID.Uint64())

			result, err := d.GetProjectByID(ctx, db, project.ID.Uint64())
			convey.So(err, convey.ShouldBeNil)
			convey.So(result.TenantID.Uint64(), convey.ShouldEqual, project.TenantID.Uint64())
		})
	})
}

func TestProjectDao_ListActiveProjectsByOrgID(t *testing.T) {
	d := &ProjectDao{}
	ctx := context.TODO()
	orgID := entity.ID(10010)

	convey.Convey("ListActiveProjectsByOrgID", t, func() {
		project1 := entity.Project{
			TenantID:       orgID,
			Name:           "test",
			Status:         "active",
			DeletedTime:    nil,
			IsCrossAccount: false,
		}
		project2 := entity.Project{
			TenantID:       orgID,
			Name:           "test",
			Status:         "deleted",
			DeletedTime:    nil,
			IsCrossAccount: false,
		}
		err := db.Create(&project1).Error
		convey.So(err, convey.ShouldBeNil)
		defer deleteProjectByID(project1.ID.Uint64())

		err = db.Create(&project2).Error
		convey.So(err, convey.ShouldBeNil)
		defer deleteProjectByID(project2.ID.Uint64())

		projects, err := d.ListActiveProjectsByOrgID(ctx, db, orgID.Uint64())
		convey.So(err, convey.ShouldBeNil)
		convey.So(len(projects), convey.ShouldEqual, 1)
	})
}

func TestProjectDao_ListProjects(t *testing.T) {
	d := &ProjectDao{}
	ctx := context.TODO()
	orgID1 := entity.ID(10010)
	orgID2 := entity.ID(10011)

	convey.Convey("ListProjects", t, func() {
		project1 := entity.Project{
			TenantID:       orgID1,
			Name:           "test",
			Status:         "active",
			DeletedTime:    nil,
			IsCrossAccount: false,
		}
		project2 := entity.Project{
			TenantID:       orgID2,
			Name:           "test",
			Status:         "deleted",
			DeletedTime:    nil,
			IsCrossAccount: false,
		}
		err := db.Create(&project1).Error
		convey.So(err, convey.ShouldBeNil)
		defer deleteProjectByID(project1.ID.Uint64())

		err = db.Create(&project2).Error
		convey.So(err, convey.ShouldBeNil)
		defer deleteProjectByID(project2.ID.Uint64())

		convey.Convey("by status only", func() {
			var orgIDs []uint64
			status := []string{"deleted"}
			var projectIDs []uint64
			projects, err := d.ListProjects(ctx, db, orgIDs, projectIDs, status)
			convey.So(err, convey.ShouldBeNil)
			convey.So(len(projects), convey.ShouldEqual, 1)
		})

		convey.Convey("by orgID only", func() {
			var status []string
			orgIDs := []uint64{orgID1.Uint64()}
			var projectIDs []uint64
			projects, err := d.ListProjects(ctx, db, orgIDs, projectIDs, status)
			convey.So(err, convey.ShouldBeNil)
			convey.So(len(projects), convey.ShouldEqual, 1)
		})

		convey.Convey("by projectID only", func() {
			projectIDs := []uint64{project1.ID.Uint64()}
			projects, err := d.ListProjects(ctx, db, nil, projectIDs, nil)
			convey.So(err, convey.ShouldBeNil)
			convey.So(len(projects), convey.ShouldEqual, 1)
		})

		convey.Convey("by orgIDs and status", func() {
			status := []string{"active"}
			orgIDs := []uint64{orgID1.Uint64()}
			projectIDs := []uint64{project1.ID.Uint64()}
			projects, err := d.ListProjects(ctx, db, orgIDs, projectIDs, status)
			convey.So(err, convey.ShouldBeNil)
			convey.So(len(projects), convey.ShouldEqual, 1)
		})

		convey.Convey("list all", func() {
			projects, err := d.ListProjects(ctx, db, nil, nil, nil)
			convey.So(err, convey.ShouldBeNil)
			convey.So(len(projects), convey.ShouldBeGreaterThanOrEqualTo, 2)
		})
	})
}

func TestProjectDao_MarkProjectAsDeletedByID(t *testing.T) {
	d := NewDao()
	ctx := context.TODO()

	convey.Convey("mark project as deleted", t, func() {
		convey.Convey("project not found", func() {
			err := d.MarkProjectAsDeletedByID(ctx, db, 0)
			convey.So(err, convey.ShouldNotBeNil)
		})

		convey.Convey("invalid project status", func() {
			project1 := entity.Project{
				TenantID:       1,
				Name:           "test",
				Status:         "deleted",
				DeletedTime:    nil,
				IsCrossAccount: false,
			}
			err := db.Create(&project1).Error
			convey.So(err, convey.ShouldBeNil)
			defer deleteProjectByID(project1.ID.Uint64())

			err = d.MarkProjectAsDeletedByID(ctx, db, project1.ID.Uint64())
			convey.So(err, convey.ShouldNotBeNil)
		})

		convey.Convey("valid project status", func() {
			project1 := entity.Project{
				TenantID:       1,
				Name:           "test",
				Status:         "active",
				DeletedTime:    nil,
				IsCrossAccount: false,
			}
			err := db.Create(&project1).Error
			convey.So(err, convey.ShouldBeNil)
			defer deleteProjectByID(project1.ID.Uint64())

			err = d.MarkProjectAsDeletedByID(ctx, db, project1.ID.Uint64())
			convey.So(err, convey.ShouldBeNil)
		})
	})
}
