package security_test

import (
	"testing"

	"github.com/enmanuel/agents/pkg/security"
)

// helpers

func makePolicy(userGroups []security.UserGroup, agentGroups []security.AgentGroup, policies []security.AgentPolicy) security.SecurityPolicy {
	return security.SecurityPolicy{
		UserGroups:  userGroups,
		AgentGroups: agentGroups,
		Policies:    policies,
	}
}

// 2.1 — sin política → ACL vacía → todo permitido (acl.Empty() == true)
func TestResolveACL_EmptyPolicy(t *testing.T) {
	a := security.ResolveACL("assistant-bot", security.SecurityPolicy{})
	if !a.Empty() {
		t.Fatal("expected empty ACL for empty policy")
	}
	if !a.CanDo("@anyone:matrix.org", "anything") {
		t.Fatal("empty ACL should allow everything")
	}
}

// 2.2 — agente en grupo → recibe los permisos del grupo
func TestResolveACL_AgentInGroup(t *testing.T) {
	p := makePolicy(
		[]security.UserGroup{{Name: "admins", Members: []string{"@alice:matrix.org"}}},
		[]security.AgentGroup{{Name: "bots", Agents: []string{"assistant-bot"}}},
		[]security.AgentPolicy{{
			AgentGroup: "bots",
			Permissions: []security.Permission{
				{UserGroup: "admins", Actions: []string{"ask"}},
			},
		}},
	)
	a := security.ResolveACL("assistant-bot", p)
	if a.Empty() {
		t.Fatal("ACL should not be empty")
	}
	if !a.CanDo("@alice:matrix.org", "ask") {
		t.Fatal("alice should be able to ask")
	}
	if a.CanDo("@bob:matrix.org", "ask") {
		t.Fatal("bob should not be able to ask")
	}
}

// 2.3 — agente NO en ningún grupo → ACL vacía
func TestResolveACL_AgentNotInGroup(t *testing.T) {
	p := makePolicy(
		[]security.UserGroup{{Name: "admins", Members: []string{"@alice:matrix.org"}}},
		[]security.AgentGroup{{Name: "bots", Agents: []string{"other-bot"}}},
		[]security.AgentPolicy{{
			AgentGroup:  "bots",
			Permissions: []security.Permission{{UserGroup: "admins", Actions: []string{"ask"}}},
		}},
	)
	a := security.ResolveACL("assistant-bot", p)
	if !a.Empty() {
		t.Fatal("expected empty ACL for agent not in any group")
	}
}

// 2.4 — wildcard de agente "*" → todos los agentes reciben los permisos
func TestResolveACL_AgentWildcard(t *testing.T) {
	p := makePolicy(
		[]security.UserGroup{{Name: "everyone", Members: []string{"*"}}},
		[]security.AgentGroup{{Name: "all", Agents: []string{"*"}}},
		[]security.AgentPolicy{{
			AgentGroup:  "all",
			Permissions: []security.Permission{{UserGroup: "everyone", Actions: []string{"ask"}}},
		}},
	)
	for _, agentID := range []string{"assistant-bot", "asistente-2", "any-random-bot"} {
		a := security.ResolveACL(agentID, p)
		if a.Empty() {
			t.Fatalf("ACL for %q should not be empty", agentID)
		}
		if !a.CanDo("@whoever:matrix.org", "ask") {
			t.Fatalf("any user should be able to ask via agent %q", agentID)
		}
	}
}

// 2.5 — wildcard de usuario "*" → todos los usuarios reciben la acción
func TestResolveACL_UserWildcard(t *testing.T) {
	p := makePolicy(
		[]security.UserGroup{{Name: "everyone", Members: []string{"*"}}},
		[]security.AgentGroup{{Name: "bots", Agents: []string{"assistant-bot"}}},
		[]security.AgentPolicy{{
			AgentGroup:  "bots",
			Permissions: []security.Permission{{UserGroup: "everyone", Actions: []string{"ask"}}},
		}},
	)
	a := security.ResolveACL("assistant-bot", p)
	for _, user := range []string{"@alice:matrix.org", "@bob:example.com", "@unknown:other.server"} {
		if !a.CanDo(user, "ask") {
			t.Fatalf("user %q should be able to ask (wildcard user group)", user)
		}
	}
}

// 2.6 — múltiples grupos que incluyen al agente → permisos acumulados
func TestResolveACL_AccumulatedPermissions(t *testing.T) {
	p := makePolicy(
		[]security.UserGroup{
			{Name: "admins", Members: []string{"@alice:matrix.org"}},
			{Name: "users", Members: []string{"@bob:matrix.org"}},
		},
		[]security.AgentGroup{
			{Name: "premium", Agents: []string{"assistant-bot"}},
			{Name: "all", Agents: []string{"*"}},
		},
		[]security.AgentPolicy{
			{
				AgentGroup:  "premium",
				Permissions: []security.Permission{{UserGroup: "admins", Actions: []string{"deploy"}}},
			},
			{
				AgentGroup:  "all",
				Permissions: []security.Permission{{UserGroup: "users", Actions: []string{"ask"}}},
			},
		},
	)
	a := security.ResolveACL("assistant-bot", p)
	if !a.CanDo("@alice:matrix.org", "deploy") {
		t.Fatal("alice should be able to deploy (via premium group)")
	}
	if !a.CanDo("@bob:matrix.org", "ask") {
		t.Fatal("bob should be able to ask (via all group)")
	}
	if a.CanDo("@bob:matrix.org", "deploy") {
		t.Fatal("bob should not be able to deploy")
	}
}

// 2.7 — agente referenciado directamente por ID en AgentPolicy.AgentGroup → recibe permisos
func TestResolveACL_DirectAgentID(t *testing.T) {
	p := makePolicy(
		[]security.UserGroup{{Name: "admins", Members: []string{"@alice:matrix.org"}}},
		nil, // no named groups
		[]security.AgentPolicy{{
			AgentGroup:  "assistant-bot", // direct agent ID, no group defined
			Permissions: []security.Permission{{UserGroup: "admins", Actions: []string{"*"}}},
		}},
	)
	a := security.ResolveACL("assistant-bot", p)
	if !a.CanDo("@alice:matrix.org", "anything") {
		t.Fatal("alice should have full access via direct agent ID assignment")
	}
	// other agents should not be affected
	b := security.ResolveACL("asistente-2", p)
	if !b.Empty() {
		t.Fatal("asistente-2 should not receive permissions from direct assignment to assistant-bot")
	}
}
