package policy

import (
	"testing"

	"lol.mleku.dev/chk"
	"git.mleku.dev/mleku/nostr/encoders/hex"
	"git.mleku.dev/mleku/nostr/interfaces/signer/p8k"
)

// TestPolicyPrecedenceRules verifies the correct evaluation order and precedence
// of different policy fields, clarifying the exact behavior after fixes.
//
// Evaluation Order (as fixed):
// 1. Universal constraints (size, tags, timestamps)
// 2. Explicit denials (highest priority)
// 3. Privileged access (ONLY if no allow lists)
// 4. Exclusive allow lists (authoritative when present)
// 5. Privileged final check
// 6. Default policy
func TestPolicyPrecedenceRules(t *testing.T) {
	// Generate test keypairs
	aliceSigner := p8k.MustNew()
	if err := aliceSigner.Generate(); chk.E(err) {
		t.Fatalf("Failed to generate alice signer: %v", err)
	}
	alicePubkey := aliceSigner.Pub()

	bobSigner := p8k.MustNew()
	if err := bobSigner.Generate(); chk.E(err) {
		t.Fatalf("Failed to generate bob signer: %v", err)
	}
	bobPubkey := bobSigner.Pub()

	charlieSigner := p8k.MustNew()
	if err := charlieSigner.Generate(); chk.E(err) {
		t.Fatalf("Failed to generate charlie signer: %v", err)
	}
	charliePubkey := charlieSigner.Pub()

	// ===================================================================
	// Test 1: Deny List Has Highest Priority
	// ===================================================================
	t.Run("Deny List Overrides Everything", func(t *testing.T) {
		policy := &P{
			DefaultPolicy: "allow",
			rules: map[int]Rule{
				100: {
					Description: "Deny overrides allow and privileged",
					WriteAllow:  []string{hex.Enc(alicePubkey)}, // Alice in allow list
					WriteDeny:   []string{hex.Enc(alicePubkey)}, // But also in deny list
					Privileged:  true,                            // And it's privileged
				},
			},
		}

		// Alice creates an event (she's author, in allow list, but also in deny list)
		event := createTestEvent(t, aliceSigner, "test", 100)

		// Should be DENIED because deny list has highest priority
		allowed, err := policy.CheckPolicy("write", event, alicePubkey, "127.0.0.1")
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if allowed {
			t.Error("FAIL: User in deny list should be denied even if in allow list and privileged")
		} else {
			t.Log("PASS: Deny list correctly overrides allow list and privileged")
		}
	})

	// ===================================================================
	// Test 2: Allow List OR Privileged (Either grants access)
	// ===================================================================
	t.Run("Allow List OR Privileged Access", func(t *testing.T) {
		policy := &P{
			DefaultPolicy: "allow",
			rules: map[int]Rule{
				200: {
					Description: "Privileged with allow list",
					ReadAllow:   []string{hex.Enc(bobPubkey)}, // Only Bob in allow list
					Privileged:  true,
				},
			},
		}

		// Alice creates event
		event := createTestEvent(t, aliceSigner, "secret", 200)

		// Test 2a: Alice is author (privileged) but NOT in allow list - should be ALLOWED (OR logic)
		allowed, err := policy.CheckPolicy("read", event, alicePubkey, "127.0.0.1")
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if !allowed {
			t.Error("FAIL: Author should be allowed via privileged (OR logic)")
		} else {
			t.Log("PASS: Author allowed via privileged despite not in allow list (OR logic)")
		}

		// Test 2b: Bob is in allow list - should be ALLOWED
		allowed, err = policy.CheckPolicy("read", event, bobPubkey, "127.0.0.1")
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if !allowed {
			t.Error("FAIL: User in allow list should be allowed")
		} else {
			t.Log("PASS: User in allow list correctly allowed")
		}

		// Test 2c: Charlie in p-tag but not in allow list - should be ALLOWED (OR logic)
		addPTag(event, charliePubkey)
		allowed, err = policy.CheckPolicy("read", event, charliePubkey, "127.0.0.1")
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if !allowed {
			t.Error("FAIL: User in p-tag should be allowed via privileged (OR logic)")
		} else {
			t.Log("PASS: User in p-tag allowed via privileged despite not in allow list (OR logic)")
		}
	})

	// ===================================================================
	// Test 3: Privileged Without Allow List Grants Access
	// ===================================================================
	t.Run("Privileged Grants Access When No Allow List", func(t *testing.T) {
		policy := &P{
			DefaultPolicy: "deny", // Default deny to make test clearer
			rules: map[int]Rule{
				300: {
					Description: "Privileged without allow list",
					Privileged:  true,
					// NO ReadAllow or WriteAllow specified
				},
			},
		}

		// Alice creates event with Bob in p-tag
		event := createTestEvent(t, aliceSigner, "message", 300)
		addPTag(event, bobPubkey)

		// Test 3a: Alice (author) should be ALLOWED (privileged, no allow list)
		allowed, err := policy.CheckPolicy("read", event, alicePubkey, "127.0.0.1")
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}


		if !allowed {
			t.Error("FAIL: Author should be allowed when privileged and no allow list")
		} else {
			t.Log("PASS: Privileged correctly grants access to author when no allow list")
		}

		// Test 3b: Bob (in p-tag) should be ALLOWED (privileged, no allow list)
		allowed, err = policy.CheckPolicy("read", event, bobPubkey, "127.0.0.1")
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if !allowed {
			t.Error("FAIL: P-tagged user should be allowed when privileged and no allow list")
		} else {
			t.Log("PASS: Privileged correctly grants access to p-tagged user when no allow list")
		}

		// Test 3c: Charlie (not involved) should be DENIED
		allowed, err = policy.CheckPolicy("read", event, charliePubkey, "127.0.0.1")
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if allowed {
			t.Error("FAIL: Non-involved user should be denied for privileged event")
		} else {
			t.Log("PASS: Privileged correctly denies non-involved user")
		}
	})

	// ===================================================================
	// Test 4: Allow List Without Privileged Is Exclusive
	// ===================================================================
	t.Run("Allow List Exclusive Without Privileged", func(t *testing.T) {
		policy := &P{
			DefaultPolicy: "allow", // Even with allow default
			rules: map[int]Rule{
				400: {
					Description: "Allow list only",
					WriteAllow:  []string{hex.Enc(alicePubkey)}, // Only Alice
					// NO Privileged flag
				},
			},
		}

		// Test 4a: Alice should be ALLOWED (in allow list)
		aliceEvent := createTestEvent(t, aliceSigner, "alice msg", 400)
		allowed, err := policy.CheckPolicy("write", aliceEvent, alicePubkey, "127.0.0.1")
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if !allowed {
			t.Error("FAIL: User in allow list should be allowed")
		} else {
			t.Log("PASS: Allow list correctly allows listed user")
		}

		// Test 4b: Bob should be DENIED (not in allow list, even with allow default)
		bobEvent := createTestEvent(t, bobSigner, "bob msg", 400)
		allowed, err = policy.CheckPolicy("write", bobEvent, bobPubkey, "127.0.0.1")
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if allowed {
			t.Error("FAIL: User not in allow list should be denied despite allow default")
		} else {
			t.Log("PASS: Allow list correctly excludes non-listed user")
		}
	})

	// ===================================================================
	// Test 5: Complex Precedence Chain
	// ===================================================================
	t.Run("Complex Precedence Chain", func(t *testing.T) {
		policy := &P{
			DefaultPolicy: "allow",
			rules: map[int]Rule{
				500: {
					Description: "Complex rules",
					WriteAllow:  []string{hex.Enc(alicePubkey), hex.Enc(bobPubkey)},
					WriteDeny:   []string{hex.Enc(bobPubkey)}, // Bob denied despite being in allow
					Privileged:  true,
				},
			},
		}

		// Test 5a: Alice in allow, not in deny - ALLOWED
		aliceEvent := createTestEvent(t, aliceSigner, "alice", 500)
		allowed, err := policy.CheckPolicy("write", aliceEvent, alicePubkey, "127.0.0.1")
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if !allowed {
			t.Error("FAIL: Alice should be allowed (in allow, not in deny)")
		} else {
			t.Log("PASS: User in allow and not in deny is allowed")
		}

		// Test 5b: Bob in allow AND deny - DENIED (deny wins)
		bobEvent := createTestEvent(t, bobSigner, "bob", 500)
		allowed, err = policy.CheckPolicy("write", bobEvent, bobPubkey, "127.0.0.1")
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if allowed {
			t.Error("FAIL: Bob should be denied (deny list overrides allow list)")
		} else {
			t.Log("PASS: Deny list correctly overrides allow list")
		}

		// Test 5c: Charlie not in allow - DENIED (even though he's author of his event)
		charlieEvent := createTestEvent(t, charlieSigner, "charlie", 500)
		allowed, err = policy.CheckPolicy("write", charlieEvent, charliePubkey, "127.0.0.1")
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if allowed {
			t.Error("FAIL: Charlie should be denied (not in allow list)")
		} else {
			t.Log("PASS: Allow list correctly excludes non-listed privileged author")
		}
	})

	// ===================================================================
	// Test 6: Default Policy Application
	// ===================================================================
	t.Run("Default Policy Only When No Rules", func(t *testing.T) {
		// Test 6a: With allow default and no rules
		policyAllow := &P{
			DefaultPolicy: "allow",
			rules: map[int]Rule{
				// No rule for kind 600
			},
		}

		event := createTestEvent(t, aliceSigner, "test", 600)
		allowed, err := policyAllow.CheckPolicy("write", event, alicePubkey, "127.0.0.1")
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if !allowed {
			t.Error("FAIL: Default allow should permit when no rules")
		} else {
			t.Log("PASS: Default allow correctly applied when no rules")
		}

		// Test 6b: With deny default and no rules
		policyDeny := &P{
			DefaultPolicy: "deny",
			rules: map[int]Rule{
				// No rule for kind 600
			},
		}

		allowed, err = policyDeny.CheckPolicy("write", event, alicePubkey, "127.0.0.1")
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if allowed {
			t.Error("FAIL: Default deny should block when no rules")
		} else {
			t.Log("PASS: Default deny correctly applied when no rules")
		}

		// Test 6c: Default does NOT apply when allow list exists
		policyWithRule := &P{
			DefaultPolicy: "allow", // Allow default
			rules: map[int]Rule{
				700: {
					WriteAllow: []string{hex.Enc(bobPubkey)}, // Only Bob
				},
			},
		}

		eventKind700 := createTestEvent(t, aliceSigner, "alice", 700)
		allowed, err = policyWithRule.CheckPolicy("write", eventKind700, alicePubkey, "127.0.0.1")
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if allowed {
			t.Error("FAIL: Default allow should NOT override exclusive allow list")
		} else {
			t.Log("PASS: Allow list correctly overrides default policy")
		}
	})
}