package system

import (
	"context"
	"testing"

	"kra/internal/biz"
	"kra/internal/conf"
)

func TestPermissionSelectedButtonsEmptyShape(t *testing.T) {
	db, err := openWithDriver("sqlite", "file:"+t.Name()+"?mode=memory&cache=shared")
	if err != nil {
		t.Fatal(err)
	}
	if err := db.AutoMigrate(&authorityButtonPO{}); err != nil {
		t.Fatal(err)
	}
	data := &Data{gormDB: newReloadableDB(db, nil), runtime: conf.NewRuntime(&conf.Data{Database: &conf.Data_Database{Driver: "sqlite"}}, &conf.AdminBackend{})}
	t.Cleanup(func() { data.gormDB.close() })

	ids, err := (&permissionRepo{data: data}).SelectedButtons(context.Background(), 1, 2)
	if err != nil {
		t.Fatal(err)
	}
	if ids != nil {
		t.Fatalf("selected buttons = %#v, want nil for an empty compatible response", ids)
	}
}

func TestPermissionCanRemoveButtonUsesRawQueryID(t *testing.T) {
	db, err := openWithDriver("sqlite", "file:"+t.Name()+"?mode=memory&cache=shared")
	if err != nil {
		t.Fatal(err)
	}
	if err := db.AutoMigrate(&authorityButtonPO{}); err != nil {
		t.Fatal(err)
	}
	data := &Data{gormDB: newReloadableDB(db, nil), runtime: conf.NewRuntime(&conf.Data{Database: &conf.Data_Database{Driver: "sqlite"}}, &conf.AdminBackend{})}
	t.Cleanup(func() { data.gormDB.close() })
	repo := &permissionRepo{data: data}
	if err := data.gormDB.WithContext(context.Background()).Create(&authorityButtonPO{AuthorityID: 888, MenuID: 10, ButtonID: 31}).Error; err != nil {
		t.Fatal(err)
	}

	allowed, err := repo.CanRemoveButton(context.Background(), "31")
	if err != nil {
		t.Fatal(err)
	}
	if allowed {
		t.Fatal("assigned button was reported removable")
	}
	allowed, err = repo.CanRemoveButton(context.Background(), "")
	if err != nil {
		t.Fatal(err)
	}
	if !allowed {
		t.Fatal("empty query ID should not match an assigned button")
	}
}

func TestPermissionSetSelectedButtonsPreservesDuplicates(t *testing.T) {
	db, err := openWithDriver("sqlite", "file:"+t.Name()+"?mode=memory&cache=shared")
	if err != nil {
		t.Fatal(err)
	}
	if err := db.AutoMigrate(&authorityButtonPO{}); err != nil {
		t.Fatal(err)
	}
	data := &Data{gormDB: newReloadableDB(db, nil), runtime: conf.NewRuntime(&conf.Data{Database: &conf.Data_Database{Driver: "sqlite"}}, &conf.AdminBackend{})}
	t.Cleanup(func() { data.gormDB.close() })
	repo := &permissionRepo{data: data}

	if err := repo.SetSelectedButtons(context.Background(), 888, 10, []uint{31, 31}); err != nil {
		t.Fatal(err)
	}
	selected, err := repo.SelectedButtons(context.Background(), 888, 10)
	if err != nil {
		t.Fatal(err)
	}
	if len(selected) != 2 || selected[0] != 31 || selected[1] != 31 {
		t.Fatalf("selected = %#v, want duplicate button IDs preserved", selected)
	}
}

func TestSetSelectedButtonsStrictRequiresManagedRoleAndOwnedButton(t *testing.T) {
	data := newMenuTestData(t)
	enableStrictAuthorityTestMode(data)
	db := data.gormDB.WithContext(context.Background())
	rootID, actorID, childID, siblingID := uint(888), uint(6001), uint(6002), uint(7001)
	if err := db.Create(&[]authorityPO{
		{AuthorityID: rootID, ParentID: authorityUintPointer(0)},
		{AuthorityID: actorID, ParentID: &rootID},
		{AuthorityID: childID, ParentID: &actorID},
		{AuthorityID: siblingID, ParentID: &rootID},
	}).Error; err != nil {
		t.Fatal(err)
	}
	if err := db.Create(&menuPO{ID: 10, Name: "managed", Path: "managed"}).Error; err != nil {
		t.Fatal(err)
	}
	if err := db.Create(&[]menuButtonPO{
		{ID: 31, MenuID: 10, Name: "owned"},
		{ID: 32, MenuID: 10, Name: "unowned"},
	}).Error; err != nil {
		t.Fatal(err)
	}
	if err := db.Create(&authorityButtonPO{AuthorityID: actorID, MenuID: 10, ButtonID: 31}).Error; err != nil {
		t.Fatal(err)
	}
	ctx := biz.NewActorContext(context.Background(), biz.Actor{AuthorityID: actorID})
	repo := &permissionRepo{data: data}
	if err := repo.SetSelectedButtons(ctx, siblingID, 10, []uint{31}); err == nil {
		t.Fatal("SetSelectedButtons() accepted an out-of-scope authority")
	}
	if err := repo.SetSelectedButtons(ctx, childID, 10, []uint{32}); err == nil {
		t.Fatal("SetSelectedButtons() accepted a button not assigned to the actor")
	}
	if err := repo.SetSelectedButtons(ctx, childID, 10, []uint{31}); err != nil {
		t.Fatalf("SetSelectedButtons() rejected a managed assignment: %v", err)
	}
}
