package system

import (
	"context"
	"testing"

	"kra/internal/biz"
)

func TestDictionaryListPreservesPreloadCollectionShapes(t *testing.T) {
	data := newTransactionTestData(t)
	ctx := context.Background()
	repo := &dictionaryRepo{data: data}

	if err := repo.CreateDictionary(ctx, &biz.Dictionary{Name: "status", Type: "status"}); err != nil {
		t.Fatal(err)
	}
	if err := repo.CreateDictionary(ctx, &biz.Dictionary{Name: "kind", Type: "kind"}); err != nil {
		t.Fatal(err)
	}

	plain, _, err := repo.ListDictionaries(ctx, 1, 1, "", "", false)
	if err != nil {
		t.Fatal(err)
	}
	if len(plain) != 2 || plain[0].Children == nil || len(plain[0].Children) != 0 {
		t.Fatalf("ordinary dictionary list children = %#v, want an empty preloaded slice", plain)
	}
	if plain[0].Details != nil {
		t.Fatalf("ordinary dictionary list details = %#v, want nil", plain[0].Details)
	}

	withDetails, _, err := repo.ListDictionaries(ctx, 1, 1, "", "", true)
	if err != nil {
		t.Fatal(err)
	}
	if len(withDetails) != 2 || withDetails[0].Details == nil || len(withDetails[0].Details) != 0 {
		t.Fatalf("dictionary details preload = %#v, want an empty preloaded slice", withDetails)
	}
	if withDetails[0].Children != nil {
		t.Fatalf("details-only dictionary list children = %#v, want nil", withDetails[0].Children)
	}
}

func TestImportDictionaryRejectsDuplicateType(t *testing.T) {
	data := newTransactionTestData(t)
	ctx := context.Background()
	repo := &dictionaryRepo{data: data}

	if err := repo.CreateDictionary(ctx, &biz.Dictionary{Name: "first", Type: "duplicate"}); err != nil {
		t.Fatal(err)
	}
	if err := repo.ImportDictionary(ctx, &biz.Dictionary{Name: "second", Type: "duplicate"}, nil); err == nil || err.Error() != "存在相同的type，不允许导入" {
		t.Fatalf("duplicate import error = %v", err)
	}

	var count int64
	if err := data.gormDB.WithContext(ctx).Model(&dictionaryPO{}).Where("type = ?", "duplicate").Count(&count).Error; err != nil {
		t.Fatal(err)
	}
	if count != 1 {
		t.Fatalf("duplicate dictionary count = %d, want 1", count)
	}
}

func TestDictionaryTreeAndParentQueriesPreserveLoadedChildrenShape(t *testing.T) {
	data := newTransactionTestData(t)
	ctx := context.Background()
	repo := &dictionaryRepo{data: data}

	dictionary := &biz.Dictionary{Name: "tree", Type: "tree"}
	if err := repo.CreateDictionary(ctx, dictionary); err != nil {
		t.Fatal(err)
	}
	parent := &biz.DictionaryDetail{Label: "parent", Value: "parent", DictionaryID: dictionary.ID, Sort: 2}
	if err := repo.CreateDictionaryDetail(ctx, parent); err != nil {
		t.Fatal(err)
	}
	parentID := parent.ID
	child := &biz.DictionaryDetail{Label: "child", Value: "child", DictionaryID: dictionary.ID, ParentID: &parentID, Sort: 1}
	if err := repo.CreateDictionaryDetail(ctx, child); err != nil {
		t.Fatal(err)
	}

	tree, err := repo.DictionaryDetailTree(ctx, dictionary.ID, "")
	if err != nil {
		t.Fatal(err)
	}
	if len(tree) != 1 || len(tree[0].Children) != 1 || tree[0].Children[0].Children == nil {
		t.Fatalf("dictionary tree = %#v, want recursively loaded child slices", tree)
	}

	flat, err := repo.DictionaryDetailsByParent(ctx, dictionary.ID, &parentID, false)
	if err != nil {
		t.Fatal(err)
	}
	if len(flat) != 1 || flat[0].Children != nil {
		t.Fatalf("flat parent query = %#v, want nil children", flat)
	}

	loaded, err := repo.DictionaryDetailsByParent(ctx, dictionary.ID, &parentID, true)
	if err != nil {
		t.Fatal(err)
	}
	if len(loaded) != 1 || loaded[0].Children == nil || len(loaded[0].Children) != 0 {
		t.Fatalf("recursive parent query = %#v, want an empty loaded child slice", loaded)
	}
}

func TestDictionaryDetailListAppliesZeroLimit(t *testing.T) {
	data := newTransactionTestData(t)
	ctx := context.Background()
	repo := &dictionaryRepo{data: data}

	dictionary := &biz.Dictionary{Name: "limited", Type: "limited"}
	if err := repo.CreateDictionary(ctx, dictionary); err != nil {
		t.Fatal(err)
	}
	if err := repo.CreateDictionaryDetail(ctx, &biz.DictionaryDetail{Label: "one", Value: "one", DictionaryID: dictionary.ID}); err != nil {
		t.Fatal(err)
	}

	items, total, err := repo.ListDictionaryDetails(ctx, 0, 0, biz.DictionaryDetailFilter{DictionaryID: dictionary.ID})
	if err != nil {
		t.Fatal(err)
	}
	if total != 1 || len(items) != 0 {
		t.Fatalf("zero-limit detail list: total=%d items=%#v", total, items)
	}
}

func TestUserListAppliesZeroLimitForNegativePageSize(t *testing.T) {
	data := newTransactionTestData(t)
	ctx := context.Background()
	if err := data.gormDB.WithContext(ctx).Create(&userPO{Username: "limited", Password: "hash", AuthorityID: 888, Enable: 1}).Error; err != nil {
		t.Fatal(err)
	}

	items, total, err := (&userRepo{data: data}).ListUsers(ctx, 1, -1, &biz.UserListFilter{})
	if err != nil {
		t.Fatal(err)
	}
	if total != 1 || len(items) != 0 {
		t.Fatalf("negative-size user list: total=%d items=%#v", total, items)
	}
}

func TestDepartmentTreeAndFlatResultsPreserveChildrenShape(t *testing.T) {
	data := newTransactionTestData(t)
	ctx := context.Background()
	repo := &departmentRepo{data: data}

	root := &biz.Department{Name: "root"}
	if err := repo.CreateDepartment(ctx, root); err != nil {
		t.Fatal(err)
	}
	var rootPO departmentPO
	if err := data.gormDB.WithContext(ctx).Where("name = ?", "root").First(&rootPO).Error; err != nil {
		t.Fatal(err)
	}
	if err := repo.CreateDepartment(ctx, &biz.Department{Name: "child", ParentID: rootPO.ID}); err != nil {
		t.Fatal(err)
	}

	tree, err := repo.ListDepartments(ctx, "")
	if err != nil {
		t.Fatal(err)
	}
	if len(tree) != 1 || len(tree[0].Children) != 1 || tree[0].Children[0].Children == nil || len(tree[0].Children[0].Children) != 0 {
		t.Fatalf("department tree children = %#v, want every loaded node to expose a slice", tree)
	}

	flat, err := repo.ListDepartments(ctx, "child")
	if err != nil {
		t.Fatal(err)
	}
	if len(flat) != 1 || flat[0].Children != nil {
		t.Fatalf("flat department search children = %#v, want nil", flat)
	}

	found, err := repo.FindDepartment(ctx, rootPO.ID)
	if err != nil {
		t.Fatal(err)
	}
	if found.Children != nil {
		t.Fatalf("single department children = %#v, want nil", found.Children)
	}
}
