package audit_test

import (
	"context"
	"strings"
	"testing"
	"time"

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

// mockLogger implements the logger interface for testing
type mockLogger struct {
	errors []string
	warns  []string
}

func (m *mockLogger) Error(msg string, keysAndValues ...any) {
	m.errors = append(m.errors, msg)
}

func (m *mockLogger) Warn(msg string, keysAndValues ...any) {
	m.warns = append(m.warns, msg)
}

func TestLoggingAlerter_CriticalAlert(t *testing.T) {
	mock := &mockLogger{}
	alerter := audit.NewLoggingAlerter(mock)

	alert := audit.TamperAlert{
		DetectedAt:   time.Now(),
		Severity:     "critical",
		Description:  "TAMPERING DETECTED in audit log",
		FilePath:     "/var/log/audit/audit.log",
		ExpectedHash: "abc123",
		ActualHash:   "def456",
	}

	err := alerter.Alert(context.Background(), alert)
	if err != nil {
		t.Fatalf("Alert failed: %v", err)
	}

	if len(mock.errors) != 1 {
		t.Errorf("Expected 1 error log, got %d", len(mock.errors))
	}
	if !strings.Contains(mock.errors[0], "TAMPERING DETECTED") {
		t.Errorf("Expected error to contain 'TAMPERING DETECTED', got %s", mock.errors[0])
	}
	if len(mock.warns) != 0 {
		t.Errorf("Expected 0 warn logs, got %d", len(mock.warns))
	}
}

func TestLoggingAlerter_WarningAlert(t *testing.T) {
	mock := &mockLogger{}
	alerter := audit.NewLoggingAlerter(mock)

	alert := audit.TamperAlert{
		DetectedAt:  time.Now(),
		Severity:    "warning",
		Description: "Potential tampering detected",
		FilePath:    "/var/log/audit/audit.log",
	}

	err := alerter.Alert(context.Background(), alert)
	if err != nil {
		t.Fatalf("Alert failed: %v", err)
	}

	if len(mock.warns) != 1 {
		t.Errorf("Expected 1 warn log, got %d", len(mock.warns))
	}
	if !strings.Contains(mock.warns[0], "Potential tampering") {
		t.Errorf("Expected warn to contain 'Potential tampering', got %s", mock.warns[0])
	}
	if len(mock.errors) != 0 {
		t.Errorf("Expected 0 error logs, got %d", len(mock.errors))
	}
}

func TestLoggingAlerter_NilLogger(t *testing.T) {
	alerter := audit.NewLoggingAlerter(nil)

	alert := audit.TamperAlert{
		DetectedAt:  time.Now(),
		Severity:    "critical",
		Description: "Test",
	}

	// Should not panic or error
	err := alerter.Alert(context.Background(), alert)
	if err != nil {
		t.Fatalf("Alert with nil logger failed: %v", err)
	}
}

func TestMultiAlerter(t *testing.T) {
	mock1 := &mockLogger{}
	mock2 := &mockLogger{}
	alerter1 := audit.NewLoggingAlerter(mock1)
	alerter2 := audit.NewLoggingAlerter(mock2)

	multi := audit.NewMultiAlerter(alerter1, alerter2)

	alert := audit.TamperAlert{
		DetectedAt:  time.Now(),
		Severity:    "critical",
		Description: "Test multi",
	}

	err := multi.Alert(context.Background(), alert)
	if err != nil {
		t.Fatalf("Multi alert failed: %v", err)
	}

	// Both alerters should have logged
	if len(mock1.errors) != 1 {
		t.Errorf("Expected alerter1 to have 1 error, got %d", len(mock1.errors))
	}
	if len(mock2.errors) != 1 {
		t.Errorf("Expected alerter2 to have 1 error, got %d", len(mock2.errors))
	}
}

func TestMultiAlerter_Empty(t *testing.T) {
	multi := audit.NewMultiAlerter()

	alert := audit.TamperAlert{
		DetectedAt:  time.Now(),
		Severity:    "critical",
		Description: "Test empty",
	}

	// Should not error with no alerters
	err := multi.Alert(context.Background(), alert)
	if err != nil {
		t.Fatalf("Multi alert with no alerters failed: %v", err)
	}
}

func TestTamperAlert_Struct(t *testing.T) {
	now := time.Now()
	alert := audit.TamperAlert{
		DetectedAt:   now,
		Severity:     "critical",
		Description:  "Test description",
		ExpectedHash: "expected",
		ActualHash:   "actual",
		FilePath:     "/path/to/file",
	}

	if alert.DetectedAt != now {
		t.Error("DetectedAt mismatch")
	}
	if alert.Severity != "critical" {
		t.Error("Severity mismatch")
	}
	if alert.Description != "Test description" {
		t.Error("Description mismatch")
	}
	if alert.ExpectedHash != "expected" {
		t.Error("ExpectedHash mismatch")
	}
	if alert.ActualHash != "actual" {
		t.Error("ActualHash mismatch")
	}
	if alert.FilePath != "/path/to/file" {
		t.Error("FilePath mismatch")
	}
}
