package security

import (
	"log/slog"
	"testing"
	"time"

	"github.com/jfraeys/fetch_ml/internal/audit"
	"github.com/jfraeys/fetch_ml/internal/logging"
)

func TestAuditLogger_ChainIntegrity(t *testing.T) {
	logger := logging.NewLogger(slog.LevelInfo, false)
	al, err := audit.NewLogger(true, "", logger)
	if err != nil {
		t.Fatalf("Failed to create audit logger: %v", err)
	}
	defer al.Close()

	// Log several events
	events := []audit.Event{
		{EventType: audit.EventFileRead, UserID: "user1", Resource: "/data/file1.txt"},
		{EventType: audit.EventFileWrite, UserID: "user1", Resource: "/data/file2.txt"},
		{EventType: audit.EventFileDelete, UserID: "user2", Resource: "/data/file3.txt"},
	}

	var loggedEvents []audit.Event
	for _, e := range events {
		al.Log(e)
		// In real scenario, we'd read back from file
		// For unit test, we just verify no panic and hashes are set
		loggedEvents = append(loggedEvents, e)
	}

	// Verify chain integrity would work if we had the actual events with hashes
	// This is a simplified test
}

func TestAuditLogger_VerifyChain(t *testing.T) {
	logger := logging.NewLogger(slog.LevelInfo, false)
	al, err := audit.NewLogger(true, "", logger)
	if err != nil {
		t.Fatalf("Failed to create audit logger: %v", err)
	}
	defer al.Close()

	// Create a valid chain of events with real hashes
	events := []audit.Event{
		{
			EventType:   audit.EventAuthSuccess,
			UserID:      "user1",
			Timestamp:   time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC),
			SequenceNum: 1,
			PrevHash:    "",
		},
		{
			EventType:   audit.EventFileRead,
			UserID:      "user1",
			Resource:    "/data/file.txt",
			Timestamp:   time.Date(2026, 1, 1, 12, 1, 0, 0, time.UTC),
			SequenceNum: 2,
		},
		{
			EventType:   audit.EventFileWrite,
			UserID:      "user1",
			Resource:    "/data/output.txt",
			Timestamp:   time.Date(2026, 1, 1, 12, 2, 0, 0, time.UTC),
			SequenceNum: 3,
		},
	}

	// Calculate real hashes for each event
	for i := range events {
		if i > 0 {
			events[i].PrevHash = events[i-1].EventHash
		}
		events[i].EventHash = al.CalculateEventHash(events[i])
	}

	// Test verification with valid chain - should pass
	tamperedSeq, err := al.VerifyChain(events)
	if err != nil {
		t.Errorf("VerifyChain failed for valid chain: %v", err)
	}
	if tamperedSeq != -1 {
		t.Errorf("Expected valid chain (tamperedSeq=-1), got %d", tamperedSeq)
	}

	// Test tamper detection - modify an event hash
	tamperedEvents := make([]audit.Event, len(events))
	copy(tamperedEvents, events)
	tamperedEvents[1].EventHash = "tampered_hash_1234567890abcdef"

	tamperedSeq, err = al.VerifyChain(tamperedEvents)
	if err == nil {
		t.Error("Expected error for tampered chain, got nil")
	}
	if tamperedSeq != 2 {
		t.Errorf("Expected tamperedSeq=2, got %d", tamperedSeq)
	}

	// Test chain break detection - modify prev_hash
	brokenEvents := make([]audit.Event, len(events))
	copy(brokenEvents, events)
	brokenEvents[1].PrevHash = "wrong_prev_hash_1234567890abcdef"

	tamperedSeq, err = al.VerifyChain(brokenEvents)
	if err == nil {
		t.Error("Expected error for broken chain, got nil")
	}
	if tamperedSeq != 2 {
		t.Errorf("Expected tamperedSeq=2 for broken chain, got %d", tamperedSeq)
	}
}

func TestAuditLogger_LogFileAccess(t *testing.T) {
	logger := logging.NewLogger(slog.LevelInfo, false)
	al, err := audit.NewLogger(true, "", logger)
	if err != nil {
		t.Fatalf("Failed to create audit logger: %v", err)
	}
	defer al.Close()

	// Test file read logging
	al.LogFileAccess(audit.EventFileRead, "user1", "/data/dataset.csv", "192.168.1.1", true, "")

	// Test file write logging
	al.LogFileAccess(audit.EventFileWrite, "user1", "/data/output.txt", "192.168.1.1", true, "")

	// Test file delete logging
	al.LogFileAccess(audit.EventFileDelete, "user2", "/data/old.txt", "192.168.1.2", false, "permission denied")
}

func TestAuditLogger_Disabled(t *testing.T) {
	logger := logging.NewLogger(slog.LevelInfo, false)
	al, err := audit.NewLogger(false, "", logger)
	if err != nil {
		t.Fatalf("Failed to create audit logger: %v", err)
	}
	defer al.Close()

	// When disabled, logging should not panic
	al.Log(audit.Event{
		EventType: audit.EventAuthSuccess,
		UserID:    "user1",
	})

	al.LogFileAccess(audit.EventFileRead, "user1", "/data/file.txt", "", true, "")
}
