package policy

import (
	"context"
	"encoding/json"
	"os"
	"path/filepath"
	"testing"

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

// TestPolicyDefinitionOfDone tests all requirements from the GitHub issue
// Issue: https://git.nostrdev.com/mleku/next.orly.dev/issues/5
//
// Requirements:
// 1. Configure relay to accept only certain kind events
// 2. Scenario A: Only certain users should be allowed to write events
// 3. Scenario B: Only certain users should be allowed to read events
// 4. Scenario C: Only users involved in events should be able to read the events (privileged)
// 5. Scenario D: Scripting option for complex validation
func TestPolicyDefinitionOfDone(t *testing.T) {
	// Generate test keypairs
	allowedSigner := p8k.MustNew()
	if err := allowedSigner.Generate(); chk.E(err) {
		t.Fatalf("Failed to generate allowed signer: %v", err)
	}
	allowedPubkey := allowedSigner.Pub()
	allowedPubkeyHex := hex.Enc(allowedPubkey)

	unauthorizedSigner := p8k.MustNew()
	if err := unauthorizedSigner.Generate(); chk.E(err) {
		t.Fatalf("Failed to generate unauthorized signer: %v", err)
	}
	unauthorizedPubkey := unauthorizedSigner.Pub()
	unauthorizedPubkeyHex := hex.Enc(unauthorizedPubkey)

	thirdPartySigner := p8k.MustNew()
	if err := thirdPartySigner.Generate(); chk.E(err) {
		t.Fatalf("Failed to generate third party signer: %v", err)
	}
	thirdPartyPubkey := thirdPartySigner.Pub()

	t.Logf("Allowed pubkey: %s", allowedPubkeyHex)
	t.Logf("Unauthorized pubkey: %s", unauthorizedPubkeyHex)

	// ===================================================================
	// Requirement 1: Configure relay to accept only certain kind events
	// ===================================================================
	t.Run("Requirement 1: Kind Whitelist", func(t *testing.T) {
		// Create policy with kind whitelist
		policyJSON := map[string]interface{}{
			"kind": map[string]interface{}{
				"whitelist": []int{1, 3, 4}, // Only allow kinds 1, 3, 4
			},
		}

		policyBytes, err := json.Marshal(policyJSON)
		if err != nil {
			t.Fatalf("Failed to marshal policy: %v", err)
		}

		policy, err := New(policyBytes)
		if err != nil {
			t.Fatalf("Failed to create policy: %v", err)
		}

		// Test: Kind 1 should be allowed (in whitelist)
		event1 := createTestEvent(t, allowedSigner, "kind 1 event", 1)
		allowed, err := policy.CheckPolicy("write", event1, allowedPubkey, "127.0.0.1")
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		if !allowed {
			t.Error("FAIL: Kind 1 should be allowed (in whitelist)")
		} else {
			t.Log("PASS: Kind 1 is allowed (in whitelist)")
		}

		// Test: Kind 5 should be denied (not in whitelist)
		event5 := createTestEvent(t, allowedSigner, "kind 5 event", 5)
		allowed, err = policy.CheckPolicy("write", event5, allowedPubkey, "127.0.0.1")
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		if allowed {
			t.Error("FAIL: Kind 5 should be denied (not in whitelist)")
		} else {
			t.Log("PASS: Kind 5 is denied (not in whitelist)")
		}

		// Test: Kind 3 should be allowed (in whitelist)
		event3 := createTestEvent(t, allowedSigner, "kind 3 event", 3)
		allowed, err = policy.CheckPolicy("write", event3, allowedPubkey, "127.0.0.1")
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		if !allowed {
			t.Error("FAIL: Kind 3 should be allowed (in whitelist)")
		} else {
			t.Log("PASS: Kind 3 is allowed (in whitelist)")
		}
	})

	// ===================================================================
	// Requirement 2: Scenario A - Only certain users can write events
	// ===================================================================
	t.Run("Scenario A: Per-Kind Write Access Control", func(t *testing.T) {
		// Create policy with write_allow for kind 10
		policyJSON := map[string]interface{}{
			"rules": map[string]interface{}{
				"10": map[string]interface{}{
					"description": "Only allowed user can write kind 10",
					"write_allow": []string{allowedPubkeyHex},
				},
			},
		}

		policyBytes, err := json.Marshal(policyJSON)
		if err != nil {
			t.Fatalf("Failed to marshal policy: %v", err)
		}

		policy, err := New(policyBytes)
		if err != nil {
			t.Fatalf("Failed to create policy: %v", err)
		}

		// Test: Allowed user can write kind 10
		event10Allowed := createTestEvent(t, allowedSigner, "kind 10 from allowed user", 10)
		allowed, err := policy.CheckPolicy("write", event10Allowed, allowedPubkey, "127.0.0.1")
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		if !allowed {
			t.Error("FAIL: Allowed user should be able to write kind 10")
		} else {
			t.Log("PASS: Allowed user can write kind 10")
		}

		// Test: Unauthorized user cannot write kind 10
		event10Unauthorized := createTestEvent(t, unauthorizedSigner, "kind 10 from unauthorized user", 10)
		allowed, err = policy.CheckPolicy("write", event10Unauthorized, unauthorizedPubkey, "127.0.0.1")
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		if allowed {
			t.Error("FAIL: Unauthorized user should NOT be able to write kind 10")
		} else {
			t.Log("PASS: Unauthorized user cannot write kind 10")
		}
	})

	// ===================================================================
	// Requirement 3: Scenario B - Only certain users can read events
	// ===================================================================
	t.Run("Scenario B: Per-Kind Read Access Control", func(t *testing.T) {
		// Create policy with read_allow for kind 20
		policyJSON := map[string]interface{}{
			"rules": map[string]interface{}{
				"20": map[string]interface{}{
					"description": "Only allowed user can read kind 20",
					"read_allow":  []string{allowedPubkeyHex},
				},
			},
		}

		policyBytes, err := json.Marshal(policyJSON)
		if err != nil {
			t.Fatalf("Failed to marshal policy: %v", err)
		}

		policy, err := New(policyBytes)
		if err != nil {
			t.Fatalf("Failed to create policy: %v", err)
		}

		// Create a kind 20 event (doesn't matter who wrote it)
		event20 := createTestEvent(t, allowedSigner, "kind 20 event", 20)

		// Test: Allowed user can read kind 20
		allowed, err := policy.CheckPolicy("read", event20, allowedPubkey, "127.0.0.1")
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		if !allowed {
			t.Error("FAIL: Allowed user should be able to read kind 20")
		} else {
			t.Log("PASS: Allowed user can read kind 20")
		}

		// Test: Unauthorized user cannot read kind 20
		allowed, err = policy.CheckPolicy("read", event20, unauthorizedPubkey, "127.0.0.1")
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		if allowed {
			t.Error("FAIL: Unauthorized user should NOT be able to read kind 20")
		} else {
			t.Log("PASS: Unauthorized user cannot read kind 20")
		}

		// Test: Unauthenticated user cannot read kind 20
		allowed, err = policy.CheckPolicy("read", event20, nil, "127.0.0.1")
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		if allowed {
			t.Error("FAIL: Unauthenticated user should NOT be able to read kind 20")
		} else {
			t.Log("PASS: Unauthenticated user cannot read kind 20")
		}
	})

	// ===================================================================
	// Requirement 4: Scenario C - Only users involved in events can read (privileged)
	// ===================================================================
	t.Run("Scenario C: Privileged Events - Only Parties Involved", func(t *testing.T) {
		// Create policy with privileged flag for kind 30
		policyJSON := map[string]interface{}{
			"rules": map[string]interface{}{
				"30": map[string]interface{}{
					"description": "Privileged - only parties involved can read",
					"privileged":  true,
				},
			},
		}

		policyBytes, err := json.Marshal(policyJSON)
		if err != nil {
			t.Fatalf("Failed to marshal policy: %v", err)
		}

		policy, err := New(policyBytes)
		if err != nil {
			t.Fatalf("Failed to create policy: %v", err)
		}

		// Test 1: Author can read their own event
		event30Author := createTestEvent(t, allowedSigner, "kind 30 authored by allowed user", 30)
		allowed, err := policy.CheckPolicy("read", event30Author, allowedPubkey, "127.0.0.1")
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		if !allowed {
			t.Error("FAIL: Author should be able to read their own privileged event")
		} else {
			t.Log("PASS: Author can read their own privileged event")
		}

		// Test 2: User in p-tag can read the event
		event30WithPTag := createTestEvent(t, allowedSigner, "kind 30 with unauthorized in p-tag", 30)
		addPTag(event30WithPTag, unauthorizedPubkey) // Add unauthorized user to p-tag
		allowed, err = policy.CheckPolicy("read", event30WithPTag, unauthorizedPubkey, "127.0.0.1")
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		if !allowed {
			t.Error("FAIL: User in p-tag should be able to read privileged event")
		} else {
			t.Log("PASS: User in p-tag can read privileged event")
		}

		// Test 3: Third party (not author, not in p-tag) cannot read
		event30NoAccess := createTestEvent(t, allowedSigner, "kind 30 for allowed only", 30)
		allowed, err = policy.CheckPolicy("read", event30NoAccess, thirdPartyPubkey, "127.0.0.1")
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		if allowed {
			t.Error("FAIL: Third party should NOT be able to read privileged event")
		} else {
			t.Log("PASS: Third party cannot read privileged event")
		}

		// Test 4: Unauthenticated user cannot read privileged event
		allowed, err = policy.CheckPolicy("read", event30NoAccess, nil, "127.0.0.1")
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		if allowed {
			t.Error("FAIL: Unauthenticated user should NOT be able to read privileged event")
		} else {
			t.Log("PASS: Unauthenticated user cannot read privileged event")
		}
	})

	// ===================================================================
	// Requirement 5: Scenario D - Scripting support
	// ===================================================================
	t.Run("Scenario D: Scripting Support", func(t *testing.T) {
		// Create temporary directory for test
		tempDir := t.TempDir()
		scriptPath := filepath.Join(tempDir, "test-policy.sh")

		// Create a simple test script that accepts events with content "accept"
		scriptContent := `#!/bin/bash
while IFS= read -r line; do
  if echo "$line" | grep -q '"content":"accept"'; then
    echo '{"id":"test","action":"accept","msg":"accepted by script"}'
  else
    echo '{"id":"test","action":"reject","msg":"rejected by script"}'
  fi
done
`
		if err := os.WriteFile(scriptPath, []byte(scriptContent), 0755); err != nil {
			t.Fatalf("Failed to write test script: %v", err)
		}

		// Create policy with script
		policyJSON := map[string]interface{}{
			"rules": map[string]interface{}{
				"40": map[string]interface{}{
					"description": "Script-based validation",
					"script":      scriptPath,
				},
			},
		}

		policyBytes, err := json.Marshal(policyJSON)
		if err != nil {
			t.Fatalf("Failed to marshal policy: %v", err)
		}

		policy, err := New(policyBytes)
		if err != nil {
			t.Fatalf("Failed to create policy: %v", err)
		}

		// Initialize policy manager
		ctx, cancel := context.WithCancel(context.Background())
		defer cancel()
		policy.manager = &PolicyManager{
			ctx:        ctx,
			cancel:     cancel,
			configDir:  tempDir,
			scriptPath: scriptPath,
			enabled:    true,
			runners:    make(map[string]*ScriptRunner),
		}

		// Test: Event with "accept" content should be accepted
		eventAccept := createTestEvent(t, allowedSigner, "accept", 40)
		allowed, err := policy.CheckPolicy("write", eventAccept, allowedPubkey, "127.0.0.1")
		if err != nil {
			t.Logf("Script check failed (expected if script not running): %v", err)
			t.Log("SKIP: Script execution requires policy manager to be fully running")
		} else if !allowed {
			t.Log("INFO: Script rejected event (may be expected if script not running)")
		} else {
			t.Log("PASS: Script accepted event with 'accept' content")
		}

		// Note: Full script testing requires the policy manager to be running,
		// which is tested in policy_integration_test.go
		t.Log("INFO: Full script validation tested in integration tests")
	})

	// ===================================================================
	// Combined Scenarios
	// ===================================================================
	t.Run("Combined: Kind Whitelist + Write Access + Privileged", func(t *testing.T) {
		// Create comprehensive policy
		policyJSON := map[string]interface{}{
			"kind": map[string]interface{}{
				"whitelist": []int{50, 51}, // Only kinds 50 and 51
			},
			"rules": map[string]interface{}{
				"50": map[string]interface{}{
					"description": "Write-restricted kind",
					"write_allow": []string{allowedPubkeyHex},
				},
				"51": map[string]interface{}{
					"description": "Privileged kind",
					"privileged":  true,
				},
			},
		}

		policyBytes, err := json.Marshal(policyJSON)
		if err != nil {
			t.Fatalf("Failed to marshal policy: %v", err)
		}

		policy, err := New(policyBytes)
		if err != nil {
			t.Fatalf("Failed to create policy: %v", err)
		}

		// Test 1: Kind 50 with allowed user should pass
		event50Allowed := createTestEvent(t, allowedSigner, "kind 50 allowed", 50)
		allowed, err := policy.CheckPolicy("write", event50Allowed, allowedPubkey, "127.0.0.1")
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		if !allowed {
			t.Error("FAIL: Kind 50 with allowed user should pass")
		} else {
			t.Log("PASS: Kind 50 with allowed user passes")
		}

		// Test 2: Kind 50 with unauthorized user should fail
		event50Unauthorized := createTestEvent(t, unauthorizedSigner, "kind 50 unauthorized", 50)
		allowed, err = policy.CheckPolicy("write", event50Unauthorized, unauthorizedPubkey, "127.0.0.1")
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		if allowed {
			t.Error("FAIL: Kind 50 with unauthorized user should fail")
		} else {
			t.Log("PASS: Kind 50 with unauthorized user fails")
		}

		// Test 3: Kind 100 (not in whitelist) should fail regardless of user
		event100 := createTestEvent(t, allowedSigner, "kind 100 not in whitelist", 100)
		allowed, err = policy.CheckPolicy("write", event100, allowedPubkey, "127.0.0.1")
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		if allowed {
			t.Error("FAIL: Kind 100 (not in whitelist) should fail")
		} else {
			t.Log("PASS: Kind 100 (not in whitelist) fails")
		}

		// Test 4: Kind 51 (privileged) - author can write
		event51Author := createTestEvent(t, allowedSigner, "kind 51 by author", 51)
		allowed, err = policy.CheckPolicy("write", event51Author, allowedPubkey, "127.0.0.1")
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		if !allowed {
			t.Error("FAIL: Author should be able to write their own privileged event")
		} else {
			t.Log("PASS: Author can write their own privileged event")
		}

		// Test 5: Kind 51 (privileged) - third party cannot read
		allowed, err = policy.CheckPolicy("read", event51Author, thirdPartyPubkey, "127.0.0.1")
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		if allowed {
			t.Error("FAIL: Third party should NOT be able to read privileged event")
		} else {
			t.Log("PASS: Third party cannot read privileged event")
		}
	})
}

// TestDefaultPolicy tests the default_policy configuration
func TestDefaultPolicy(t *testing.T) {
	allowedSigner := p8k.MustNew()
	if err := allowedSigner.Generate(); chk.E(err) {
		t.Fatalf("Failed to generate signer: %v", err)
	}

	t.Run("default policy allow", func(t *testing.T) {
		policyJSON := map[string]interface{}{
			"default_policy": "allow",
		}

		policyBytes, err := json.Marshal(policyJSON)
		if err != nil {
			t.Fatalf("Failed to marshal policy: %v", err)
		}

		policy, err := New(policyBytes)
		if err != nil {
			t.Fatalf("Failed to create policy: %v", err)
		}

		// Event without specific rule should be allowed
		event := createTestEvent(t, allowedSigner, "test event", 999)
		allowed, err := policy.CheckPolicy("write", event, allowedSigner.Pub(), "127.0.0.1")
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		if !allowed {
			t.Error("FAIL: Event should be allowed with default_policy=allow")
		} else {
			t.Log("PASS: Event allowed with default_policy=allow")
		}
	})

	t.Run("default policy deny", func(t *testing.T) {
		policyJSON := map[string]interface{}{
			"default_policy": "deny",
		}

		policyBytes, err := json.Marshal(policyJSON)
		if err != nil {
			t.Fatalf("Failed to marshal policy: %v", err)
		}

		policy, err := New(policyBytes)
		if err != nil {
			t.Fatalf("Failed to create policy: %v", err)
		}

		// Event without specific rule should be denied
		event := createTestEvent(t, allowedSigner, "test event", 999)
		allowed, err := policy.CheckPolicy("write", event, allowedSigner.Pub(), "127.0.0.1")
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		if allowed {
			t.Error("FAIL: Event should be denied with default_policy=deny")
		} else {
			t.Log("PASS: Event denied with default_policy=deny")
		}
	})
}
