package db

import (
	"context"
	"errors"
	"testing"

	"github.com/pingcap/agent-git-service/internal/tenant"
)

func TestEnforceTenantProvenanceBackfillsGuardsAndVerifiesRecords(t *testing.T) {
	gdb := openTiDB(t)
	if err := gdb.AutoMigrate(&User{}, &Repository{}, &PagesBuild{}, &AuditLogEntry{}, &WikiSearchProjectionTask{}); err != nil {
		t.Fatalf("AutoMigrate: %v", err)
	}
	owner := User{Login: "tenant-provenance-owner", Name: "Tenant Provenance Owner", Type: TypeUser}
	if err := gdb.Create(&owner).Error; err != nil {
		t.Fatalf("create owner: %v", err)
	}
	repository := Repository{
		OwnerID:       owner.ID,
		Name:          "tenant-provenance",
		FullName:      owner.Login + "/tenant-provenance",
		DefaultBranch: "main",
	}
	if err := gdb.Create(&repository).Error; err != nil {
		t.Fatalf("create repository: %v", err)
	}

	legacy := PagesBuild{RepositoryID: repository.ID, Status: "queued"}
	if err := gdb.Create(&legacy).Error; err != nil {
		t.Fatalf("create legacy record: %v", err)
	}
	if legacy.TenantID != "" {
		t.Fatalf("legacy tenant ID = %q, want empty before enforcement", legacy.TenantID)
	}
	legacyProjection := WikiSearchProjectionTask{
		RepositoryID: repository.ID,
		Slug:         "docs/home",
		Kind:         "lexical",
		Generation:   1,
	}
	if err := gdb.Create(&legacyProjection).Error; err != nil {
		t.Fatalf("create legacy projection task: %v", err)
	}
	if legacyProjection.TenantID != "" {
		t.Fatalf("legacy projection tenant ID = %q, want empty before enforcement", legacyProjection.TenantID)
	}

	tenantA := tenant.Info{
		ID:               tenant.ID("tenant-a"),
		CanonicalBaseURL: "https://tenant-a.example.test",
	}
	tenantB := tenant.Info{
		ID:               tenant.ID("tenant-b"),
		CanonicalBaseURL: "https://tenant-b.example.test",
	}
	tenantACtx := tenant.WithInfo(context.Background(), tenantA)
	if err := EnforceTenantProvenance(gdb, tenantA); err != nil {
		t.Fatalf("EnforceTenantProvenance: %v", err)
	}
	var backfilled PagesBuild
	if err := gdb.First(&backfilled, legacy.ID).Error; err != nil {
		t.Fatalf("load backfilled record: %v", err)
	}
	if backfilled.TenantID != "tenant-a" {
		t.Fatalf("backfilled tenant ID = %q, want tenant-a", backfilled.TenantID)
	}
	var backfilledProjection WikiSearchProjectionTask
	if err := gdb.First(&backfilledProjection, legacyProjection.ID).Error; err != nil {
		t.Fatalf("load backfilled projection task: %v", err)
	}
	if backfilledProjection.TenantID != "tenant-a" {
		t.Fatalf("backfilled projection tenant ID = %q, want tenant-a", backfilledProjection.TenantID)
	}

	audit := AuditLogEntry{Action: "test.event"}
	if err := gdb.WithContext(tenantACtx).Create(&audit).Error; err != nil {
		t.Fatalf("create stamped audit record: %v", err)
	}
	if audit.TenantID != "tenant-a" {
		t.Fatalf("created audit tenant ID = %q, want tenant-a", audit.TenantID)
	}

	builds := []PagesBuild{
		{RepositoryID: 1, Status: "queued"},
		{RepositoryID: 2, Status: "queued"},
	}
	if err := gdb.WithContext(tenantACtx).Create(&builds).Error; err != nil {
		t.Fatalf("create stamped record batch: %v", err)
	}
	for index, build := range builds {
		if build.TenantID != "tenant-a" {
			t.Fatalf("batch record %d tenant ID = %q, want tenant-a", index, build.TenantID)
		}
	}

	forged := PagesBuild{
		TenantProvenance: TenantProvenance{TenantID: "tenant-b"},
		RepositoryID:     3,
		Status:           "queued",
	}
	if err := gdb.WithContext(tenantACtx).Create(&forged).Error; !errors.Is(err, ErrTenantProvenanceMismatch) {
		t.Fatalf("forged create error = %v, want %v", err, ErrTenantProvenanceMismatch)
	}

	missingTenant := PagesBuild{RepositoryID: 4, Status: "queued"}
	if err := gdb.Create(&missingTenant).Error; !errors.Is(err, tenant.ErrRequired) {
		t.Fatalf("missing-context create error = %v, want %v", err, tenant.ErrRequired)
	}

	wrongTenant := PagesBuild{RepositoryID: 5, Status: "queued"}
	if err := gdb.WithContext(tenant.WithInfo(context.Background(), tenantB)).
		Create(&wrongTenant).Error; !errors.Is(err, ErrTenantProvenanceMismatch) {
		t.Fatalf("wrong-tenant create error = %v, want %v", err, ErrTenantProvenanceMismatch)
	}

	forgedPolicy := tenantA
	forgedPolicy.CanonicalBaseURL = "https://forged.example.test"
	forgedContext := PagesBuild{RepositoryID: 6, Status: "queued"}
	if err := gdb.WithContext(tenant.WithInfo(context.Background(), forgedPolicy)).
		Create(&forgedContext).Error; !errors.Is(err, ErrTenantProvenanceMismatch) {
		t.Fatalf("forged-policy create error = %v, want %v", err, ErrTenantProvenanceMismatch)
	}

	if err := gdb.Table("pages_builds").
		Where("id = ?", legacy.ID).
		UpdateColumn("tenant_id", "tenant-b").Error; err != nil {
		t.Fatalf("corrupt record provenance: %v", err)
	}
	if err := EnforceTenantProvenance(gdb, tenantA); !errors.Is(err, ErrTenantProvenanceMismatch) {
		t.Fatalf("mismatched record verification error = %v, want %v", err, ErrTenantProvenanceMismatch)
	}
}
