package data

import (
	"context"
	"errors"
	"kra/internal/biz/system"
	"testing"

	"gorm.io/gorm"
)

type dataScopeRecord struct {
	ID        uint `gorm:"primaryKey"`
	DeptID    uint
	CreatedBy uint
	Name      string
}

func (dataScopeRecord) TableName() string { return "business_scope_records" }

func newDataScopeTestDB(t *testing.T) *gorm.DB {
	t.Helper()
	db, err := openWithDriver("sqlite", "file:"+t.Name()+"?mode=memory&cache=shared")
	if err != nil {
		t.Fatal(err)
	}
	if err := db.AutoMigrate(&dataScopeRecord{}); err != nil {
		t.Fatal(err)
	}
	registerDataScopeCallbacks(db, nil)
	sqlDB, err := db.DB()
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { _ = sqlDB.Close() })
	return db
}

func TestDataScopeCallbacksFailClosedAndAllowExplicitSystemBypass(t *testing.T) {
	db := newDataScopeTestDB(t)
	seed := []dataScopeRecord{
		{DeptID: 10, CreatedBy: 7, Name: "visible"},
		{DeptID: 20, CreatedBy: 8, Name: "hidden"},
		{DeptID: 0, CreatedBy: 9, Name: "unassigned"},
	}
	if err := db.Set("data_scope:skip", true).Create(&seed).Error; err != nil {
		t.Fatal(err)
	}

	var rows []dataScopeRecord
	if err := db.WithContext(context.Background()).Find(&rows).Error; !errors.Is(err, errDataScopeRequired) {
		t.Fatalf("query without data scope error = %v", err)
	}

	invalidCtx := system.NewDataScopeContext(context.Background(), system.DataScope{UserID: 7, AuthorityID: 1, Scope: 1, All: false})
	if err := db.WithContext(invalidCtx).Find(&rows).Error; !errors.Is(err, errInvalidDataScope) {
		t.Fatalf("query with invalid data scope error = %v", err)
	}

	scopedCtx := system.NewDataScopeContext(context.Background(), system.DataScope{UserID: 7, AuthorityID: 1, Scope: 3, DepartmentIDs: []uint{10}})
	rows = nil
	if err := db.WithContext(scopedCtx).Order("id").Find(&rows).Error; err != nil {
		t.Fatal(err)
	}
	if len(rows) != 1 || rows[0].Name != "visible" {
		t.Fatalf("scoped query rows = %+v", rows)
	}

	emptyCtx := system.NewDataScopeContext(context.Background(), system.DataScope{UserID: 7, AuthorityID: 1, Scope: 5})
	rows = nil
	if err := db.WithContext(emptyCtx).Find(&rows).Error; err != nil {
		t.Fatal(err)
	}
	if len(rows) != 0 {
		t.Fatalf("empty custom scope exposed rows: %+v", rows)
	}

	rows = nil
	if err := db.Set("data_scope:skip", true).Order("id").Find(&rows).Error; err != nil {
		t.Fatal(err)
	}
	if len(rows) != len(seed) {
		t.Fatalf("explicit system bypass rows = %d, want %d", len(rows), len(seed))
	}
}

func TestDataScopeCreateRequiresIdentityAndStampsOwnership(t *testing.T) {
	db := newDataScopeTestDB(t)
	if err := db.Create(&dataScopeRecord{Name: "missing"}).Error; !errors.Is(err, errDataScopeRequired) {
		t.Fatalf("create without data scope error = %v", err)
	}

	ctx := system.NewDataScopeContext(context.Background(), system.DataScope{UserID: 7, AuthorityID: 1, Scope: 3, PrimaryDeptID: 10, DepartmentIDs: []uint{10}})
	created := dataScopeRecord{Name: "owned", DeptID: 999, CreatedBy: 999}
	if err := db.WithContext(ctx).Create(&created).Error; err != nil {
		t.Fatal(err)
	}
	var stored dataScopeRecord
	if err := db.Set("data_scope:skip", true).First(&stored, created.ID).Error; err != nil {
		t.Fatal(err)
	}
	if stored.DeptID != 10 || stored.CreatedBy != 7 {
		t.Fatalf("stamped ownership = dept:%d user:%d, want 10/7", stored.DeptID, stored.CreatedBy)
	}
}
