package repository

import (
	"testing"

	"github.com/onsi/gomega"
	"gorm.io/gorm"

	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/infra/repository/schema"
)

// TestCvtProjectMigrationTaskModelToEntity verifies the conversion from the
// GORM model to the domain entity preserves all fields.
func TestCvtProjectMigrationTaskModelToEntity(t *testing.T) {
	g := gomega.NewWithT(t)

	model := &schema.ProjectMigrationTask{
		Model: gorm.Model{ID: 3},
		OrgID: 42,
		State: "RUNNING",
		Migrations: schema.ProjectMigrationItems{
			{SourceProjectID: 10, TargetProjectID: 20, TargetProjectName: ""},
			{SourceProjectID: 11, TargetProjectID: 0, TargetProjectName: "new-proj"},
		},
	}

	got := cvtProjectMigrationTaskModelToEntity(model)

	g.Expect(got.ID).To(gomega.Equal(uint64(3)))
	g.Expect(got.OrgID).To(gomega.Equal(uint64(42)))
	g.Expect(got.State).To(gomega.Equal(entity.ProjectMigrationTaskStateRunning))
	g.Expect(got.Migrations).To(gomega.HaveLen(2))
	g.Expect(got.Migrations[0].SourceProjectID).To(gomega.Equal(uint64(10)))
	g.Expect(got.Migrations[0].TargetProjectID).To(gomega.Equal(uint64(20)))
	g.Expect(got.Migrations[1].TargetProjectName).To(gomega.Equal("new-proj"))
}

// TestCvtProjectMigrationTaskEntityToModel verifies the reverse conversion and
// that a zero ID stays zero (triggers Create path).
func TestCvtProjectMigrationTaskEntityToModel(t *testing.T) {
	g := gomega.NewWithT(t)

	ent := &entity.ProjectMigrationTask{
		ID:    0, // new record
		OrgID: 99,
		State: entity.ProjectMigrationTaskStatePending,
		Migrations: []*entity.ProjectMigration{
			{SourceProjectID: 5, TargetProjectID: 6, TargetProjectName: "t"},
		},
	}

	got := cvtProjectMigrationTaskEntityToModel(ent)

	g.Expect(got.ID).To(gomega.Equal(uint(0)))
	g.Expect(got.OrgID).To(gomega.Equal(uint64(99)))
	g.Expect(got.State).To(gomega.Equal("PENDING"))
	g.Expect(got.Migrations).To(gomega.HaveLen(1))
	g.Expect(got.Migrations[0].SourceProjectID).To(gomega.Equal(uint64(5)))
}

// TestCvtRoundTrip ensures that entity→model→entity is lossless.
func TestCvtRoundTrip(t *testing.T) {
	g := gomega.NewWithT(t)

	original := &entity.ProjectMigrationTask{
		ID:    7,
		OrgID: 1000,
		State: entity.ProjectMigrationTaskStateSucceed,
		Migrations: []*entity.ProjectMigration{
			{SourceProjectID: 100, TargetProjectID: 200, TargetProjectName: "tgt"},
		},
	}

	model := cvtProjectMigrationTaskEntityToModel(original)
	roundTripped := cvtProjectMigrationTaskModelToEntity(model)

	g.Expect(roundTripped.ID).To(gomega.Equal(original.ID))
	g.Expect(roundTripped.OrgID).To(gomega.Equal(original.OrgID))
	g.Expect(roundTripped.State).To(gomega.Equal(original.State))
	g.Expect(roundTripped.Migrations).To(gomega.HaveLen(len(original.Migrations)))
	g.Expect(roundTripped.Migrations[0].SourceProjectID).To(gomega.Equal(original.Migrations[0].SourceProjectID))
	g.Expect(roundTripped.Migrations[0].TargetProjectID).To(gomega.Equal(original.Migrations[0].TargetProjectID))
	g.Expect(roundTripped.Migrations[0].TargetProjectName).To(gomega.Equal(original.Migrations[0].TargetProjectName))
}

// TestProjectMigrationItemsJSON verifies the Scan/Value round-trip for the
// custom JSON type used to persist the migrations slice.
func TestProjectMigrationItemsJSON(t *testing.T) {
	g := gomega.NewWithT(t)

	items := schema.ProjectMigrationItems{
		{SourceProjectID: 1, TargetProjectID: 2, TargetProjectName: "p"},
	}

	// Value → raw bytes
	raw, err := items.Value()
	g.Expect(err).ToNot(gomega.HaveOccurred())

	// Scan bytes back
	var out schema.ProjectMigrationItems
	err = out.Scan(raw)
	g.Expect(err).ToNot(gomega.HaveOccurred())
	g.Expect(out).To(gomega.HaveLen(1))
	g.Expect(out[0].SourceProjectID).To(gomega.Equal(uint64(1)))
	g.Expect(out[0].TargetProjectID).To(gomega.Equal(uint64(2)))
	g.Expect(out[0].TargetProjectName).To(gomega.Equal("p"))
}

// TestProjectMigrationItemsJSON_NilRoundTrip ensures nil slices survive the round-trip.
func TestProjectMigrationItemsJSON_NilRoundTrip(t *testing.T) {
	g := gomega.NewWithT(t)

	var items schema.ProjectMigrationItems // nil

	raw, err := items.Value()
	g.Expect(err).ToNot(gomega.HaveOccurred())
	// nil returns the zero driver.Value
	g.Expect(raw).To(gomega.BeNil())

	var out schema.ProjectMigrationItems
	err = out.Scan(nil)
	g.Expect(err).ToNot(gomega.HaveOccurred())
	g.Expect(out).To(gomega.BeNil())
}
