package migrate

import (
	"context"
	"testing"

	"github.com/stretchr/testify/require"
	"github.com/tidbcloud/aisupport/services/jsm-syncer/models"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

func TestMigratorAppliesMigrationsToExistingDatabase(t *testing.T) {
	db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{})
	require.NoError(t, err)

	require.NoError(t, db.Exec(`
		CREATE TABLE tidbcloud_orgs (
			id varchar(64) PRIMARY KEY,
			name varchar(255) NOT NULL,
			status varchar(64),
			support_plan varchar(64),
			plan varchar(64),
			jsm_org_id varchar(64),
			msp_org_id varchar(64),
			msp_notification_enabled tinyint(1) NOT NULL DEFAULT 0,
			biz_region varchar(64),
			time_zone varchar(64),
			created_at TIMESTAMP,
			updated_at TIMESTAMP
		)
	`).Error)
	require.NoError(t, db.Exec("CREATE TABLE jsm_syncer_migrations (id varchar(255) PRIMARY KEY)").Error)
	require.False(t, db.Migrator().HasColumn(&models.TiDBCloudOrg{}, "msp_relation_checked_at"))

	require.NoError(t, Migrator.Run(db))

	require.True(t, db.Migrator().HasColumn(&models.TiDBCloudOrg{}, "msp_relation_checked_at"))
	require.True(t, db.Migrator().HasIndex(&models.TiDBCloudOrg{}, "idx_tidbcloud_orgs_msp_relation_checked_at"))
	require.True(t, db.Migrator().HasTable(&models.TechnicalRoleOrgWhitelist{}))
}

func TestMigratorIncludesMSPRelationCheckedAtMigration(t *testing.T) {
	require.PanicsWithValue(t, "migration version is already added: 202607091200", func() {
		addRecord("202607091200", func(db *gorm.DB) error {
			return nil
		})
	})
}

func TestDeleteSystemUsers(t *testing.T) {
	t.Parallel()

	db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{})
	require.NoError(t, err)
	require.NoError(t, db.AutoMigrate(
		&models.TiDBCloudUser{},
		&models.TiDBCloudOrgMembership{},
		&models.TiDBCloudLakeMembership{},
	))

	users := []models.TiDBCloudUser{
		{ID: "regular", Email: "regular@example.com"},
		{ID: "system", Email: "automation-system@pingcap.com"},
		{ID: "system-case-space", Email: "Automation-System@PingCAP.com "},
		{ID: "near-miss", Email: "system@pingcap.com"},
		{ID: "suffix-miss", Email: "automation-system@pingcap.com.invalid"},
	}
	require.NoError(t, db.Create(&users).Error)

	memberships := make([]models.TiDBCloudOrgMembership, 0, len(users))
	for _, user := range users {
		memberships = append(memberships, models.TiDBCloudOrgMembership{
			OrgID:  "org-" + user.ID,
			UserID: user.ID,
			Status: models.RelationStatusActive,
		})
	}
	require.NoError(t, db.Create(&memberships).Error)
	lakeMemberships := make([]models.TiDBCloudLakeMembership, 0, len(users))
	for _, user := range users {
		lakeMemberships = append(lakeMemberships, models.TiDBCloudLakeMembership{
			LakeID: "lake-" + user.ID,
			UserID: user.ID,
			Status: models.RelationStatusActive,
		})
	}
	require.NoError(t, db.Create(&lakeMemberships).Error)

	require.NoError(t, deleteSystemUsers(db.WithContext(context.Background())))

	var remainingUsers []models.TiDBCloudUser
	require.NoError(t, db.Order("id ASC").Find(&remainingUsers).Error)
	require.Equal(t, []string{"near-miss", "regular", "suffix-miss"}, userIDs(remainingUsers))

	var remainingMemberships []models.TiDBCloudOrgMembership
	require.NoError(t, db.Order("user_id ASC").Find(&remainingMemberships).Error)
	require.Equal(t, []string{"near-miss", "regular", "suffix-miss"}, membershipUserIDs(remainingMemberships))

	var remainingLakeMemberships []models.TiDBCloudLakeMembership
	require.NoError(t, db.Order("user_id ASC").Find(&remainingLakeMemberships).Error)
	require.Equal(t, []string{"near-miss", "regular", "suffix-miss"}, lakeMembershipUserIDs(remainingLakeMemberships))
}

func userIDs(users []models.TiDBCloudUser) []string {
	ids := make([]string, 0, len(users))
	for _, user := range users {
		ids = append(ids, user.ID)
	}
	return ids
}

func membershipUserIDs(memberships []models.TiDBCloudOrgMembership) []string {
	ids := make([]string, 0, len(memberships))
	for _, membership := range memberships {
		ids = append(ids, membership.UserID)
	}
	return ids
}

func lakeMembershipUserIDs(memberships []models.TiDBCloudLakeMembership) []string {
	ids := make([]string, 0, len(memberships))
	for _, membership := range memberships {
		ids = append(ids, membership.UserID)
	}
	return ids
}
