package auth

import "testing"

// installRoleFixture installs representative DB rows for the roles asserted
// across unit tests. Production grants live in the role_permissions seeds;
// this fixture only gives Can()/EffectivePermissions() tests deterministic
// rows now that resolution is DB-only (zero rows = deny-all).
func installRoleFixture(t *testing.T) {
	t.Helper()
	prev := roleMatrix.Load()
	t.Cleanup(func() { roleMatrix.Store(prev) })
	SetRoleMatrix(map[Role][]string{
		RoleSales: {
			"sales.*",
			"masterdata.customers.*", "masterdata.products.*", "masterdata.brands.*",
			"masterdata.memberships.read", "masterdata.memberships.lookup", "masterdata.memberships.create",
			"fakturisasi.*", "sales_tracking.*", "distribution.*",
			"inventory.stock.view",
			"dashboard.view", "dashboard.pos.view", "sales.reports.view",
		},
		RoleFinance: {
			"masterdata.products.read", "masterdata.categories.read", "masterdata.units.read",
			"masterdata.brands.read", "masterdata.suppliers.view", "masterdata.customers.read",
			"purchasing.purchase-orders.view", "purchasing.goods-receipts.view",
			"purchasing.vouchers.view", "purchasing.vouchers.create", "purchasing.vouchers.update",
			"fakturisasi.*", "finance.*", "accounting.*",
			"dashboard.view", "finance.reports.view", "sales.reports.view",
			"inventory.stock.view", "system.stores.view",
		},
		RoleFinanceHead: {
			"purchasing.purchase-orders.view", "purchasing.purchase-orders.approve",
		},
		RoleWarehouse: {
			"masterdata.*", "inventory.*",
			"purchasing.purchase-orders.view", "purchasing.purchase-orders.create",
			"purchasing.purchase-orders.update", "purchasing.purchase-orders.delete",
			"purchasing.purchase-orders.cancel", "purchasing.purchase-orders.pay",
			"purchasing.goods-receipts.view", "purchasing.goods-receipts.create",
			"purchasing.vouchers.view", "purchasing.vouchers.create", "purchasing.vouchers.update",
			"purchasing.debts.export", "distribution.*",
			"dashboard.view", "system.stores.view",
		},
		RoleGM: {
			"sales.*",
			"purchasing.purchase-orders.view", "purchasing.purchase-orders.create",
			"purchasing.purchase-orders.update", "purchasing.purchase-orders.delete",
			"purchasing.purchase-orders.cancel", "purchasing.purchase-orders.pay",
		},
		RoleBM: {
			"sales.*",
			"purchasing.purchase-orders.view", "purchasing.purchase-orders.create",
			"purchasing.purchase-orders.update", "purchasing.purchase-orders.delete",
			"purchasing.purchase-orders.cancel", "purchasing.purchase-orders.pay",
		},
		RoleHRManager: {"hr.*", "dashboard.view"},
		RoleHRStaff: {
			"hr.employees.*", "hr.attendance.*", "hr.leaves.*", "dashboard.view",
		},
		RoleEmployee: {
			"hr.clockin", "hr.clockout", "hr.leaves.store", "dashboard.view",
		},
		RoleSystem: {"system.*", "dashboard.view", "system.notifications.send"},
		RoleBuyer: {
			"masterdata.products.read", "masterdata.categories.read",
			"masterdata.brands.read", "masterdata.units.read",
			"masterdata.memberships.read", "cart.*",
			"galon.transactions.create", "sales.sales-vouchers.validate",
			"sales.sales-orders.cancel", "sales.returns.create",
			"distribution.tracking.lookup",
		},
		RoleCashier: {
			"sales.pos.create", "cashier.*",
			"masterdata.customers.*", "masterdata.memberships.*",
			"masterdata.products.read", "masterdata.categories.read", "masterdata.units.read", "masterdata.brands.read",
			"sales.sales-orders.view", "sales.returns.*", "sales.sales-vouchers.validate",
			"galon.transactions.*", "inventory.stock.view", "system.settings.view", "dashboard.pos.view",
		},
	})
}

func TestMatrixIsDBOnly(t *testing.T) {
	defer SetRoleMatrix(nil)

	// No matrix installed: zero rows = deny-all for every non-bypass role.
	if (&User{Role: RoleSales}).Can("inventory.stock.view") {
		t.Fatal("without DB rows sales must hold nothing")
	}
	// Admin/owner bypass needs no rows.
	if !(&User{Role: RoleAdmin}).Can("inventory.stock.view") {
		t.Fatal("admin bypass must survive an empty matrix")
	}

	SetRoleMatrix(map[Role][]string{
		RoleSales: {"inventory.stock.view"},
		RoleBuyer: {},
		// warehouse absent -> denies too (no builtin fallback).
	})

	u := &User{Role: RoleSales}
	if !u.Can("inventory.stock.view") {
		t.Fatal("matrix row should grant stock view")
	}
	if u.Can("inventory.stock-in.create") {
		t.Fatal("deny by omission: unlisted permission must not resolve")
	}
	// Explicit deny by omission: empty set = NO permissions.
	if (&User{Role: RoleBuyer}).Can("cart.add") {
		t.Fatal("empty matrix set must deny everything")
	}
	// Absent role denies (DB-only: no builtin fallback).
	if (&User{Role: RoleWarehouse}).Can("inventory.stock.view") {
		t.Fatal("role missing from the matrix must hold nothing")
	}
}

func TestMatrixResetDenies(t *testing.T) {
	SetRoleMatrix(map[Role][]string{RoleSales: {"inventory.stock.view"}})
	SetRoleMatrix(nil)
	if RoleMatrix() != nil {
		t.Fatal("nil reset should clear the matrix")
	}
	if (&User{Role: RoleSales}).Can("inventory.stock.view") {
		t.Fatal("cleared matrix must deny everything")
	}
}
