package models

import (
	"encoding/json"
	"testing"
	"time"
)

func TestLogEventParamsCreation(t *testing.T) {
	userID := "user-123"
	participantID := "participant-456"
	now := time.Now()

	params := LogEventParams{
		ID:            1,
		UserID:        &userID,
		ParticipantID: &participantID,
		ActivityType:  10,
		IPAddress:     "192.168.1.1",
		FieldUpdated:  map[string]string{"field": "value"},
		Time:          &now,
		ErrorMessage:  "",
	}

	if params.FieldUpdated == nil {
		t.Error("Expected FieldUpdated to not be nil")
	}

	if params.Time == nil {
		t.Error("Expected Time to not be nil")
	}

	if params.ErrorMessage != "" {
		t.Errorf("Expected empty ErrorMessage, got '%s'", params.ErrorMessage)
	}

	if params.ID != 1 {
		t.Errorf("Expected ID 1, got %d", params.ID)
	}

	if *params.UserID != "user-123" {
		t.Errorf("Expected UserID 'user-123', got '%s'", *params.UserID)
	}

	if *params.ParticipantID != "participant-456" {
		t.Errorf("Expected ParticipantID 'participant-456', got '%s'", *params.ParticipantID)
	}

	if params.ActivityType != 10 {
		t.Errorf("Expected ActivityType 10, got %d", params.ActivityType)
	}

	if params.IPAddress != "192.168.1.1" {
		t.Errorf("Expected IPAddress '192.168.1.1', got '%s'", params.IPAddress)
	}
}

func TestLogEventParamsNullableFields(t *testing.T) {
	params := LogEventParams{
		ID:            2,
		UserID:        nil,
		ParticipantID: nil,
		ActivityType:  5,
		IPAddress:     LocalNetwork,
		FieldUpdated:  nil,
		Time:          nil,
		ErrorMessage:  "Test error",
	}

	if params.ID != 2 {
		t.Errorf("Expected ID 2, got %d", params.ID)
	}

	if params.ActivityType != 5 {
		t.Errorf("Expected ActivityType 5, got %d", params.ActivityType)
	}

	if params.IPAddress != LocalNetwork {
		t.Errorf("Expected IPAddress '10.0.0.1', got '%s'", params.IPAddress)
	}

	if params.UserID != nil {
		t.Error("Expected UserID to be nil")
	}

	if params.ParticipantID != nil {
		t.Error("Expected ParticipantID to be nil")
	}

	if params.Time != nil {
		t.Error("Expected Time to be nil")
	}

	if params.FieldUpdated != nil {
		t.Error("Expected FieldUpdated to be nil")
	}

	if params.ErrorMessage != "Test error" {
		t.Errorf("Expected ErrorMessage 'Test error', got '%s'", params.ErrorMessage)
	}
}

func TestLogEventParamsFieldUpdatedInterface(t *testing.T) {
	// Test with map
	mapData := map[string]interface{}{"key": "value", "count": 42}
	params1 := LogEventParams{
		FieldUpdated: mapData,
	}

	if params1.FieldUpdated == nil {
		t.Error("Expected FieldUpdated to not be nil")
	}

	// Test with string
	params2 := LogEventParams{
		FieldUpdated: "simple string value",
	}

	if params2.FieldUpdated != "simple string value" {
		t.Errorf("Expected FieldUpdated 'simple string value', got '%v'", params2.FieldUpdated)
	}

	// Test with int
	params3 := LogEventParams{
		FieldUpdated: 123,
	}

	if params3.FieldUpdated != 123 {
		t.Errorf("Expected FieldUpdated 123, got %v", params3.FieldUpdated)
	}
}

func TestLogEventParamsJSONMarshaling(t *testing.T) {
	userID := "user-789"
	now := time.Now()

	params := LogEventParams{
		ID:           3,
		UserID:       &userID,
		ActivityType: 15,
		IPAddress:    "172.16.0.1",
		Time:         &now,
		ErrorMessage: "",
	}

	if params.ActivityType != 15 {
		t.Errorf("Expected ActivityType 15, got %d", params.ActivityType)
	}

	if params.IPAddress != "172.16.0.1" {
		t.Errorf("Expected IPAddress '172.16.0.1', got '%s'", params.IPAddress)
	}

	jsonData, err := json.Marshal(params)
	if err != nil {
		t.Fatalf("Failed to marshal LogEventParams: %v", err)
	}

	if len(jsonData) == 0 {
		t.Error("Expected non-empty JSON data")
	}

	// Unmarshal back
	var unmarshaled LogEventParams
	err = json.Unmarshal(jsonData, &unmarshaled)
	if err != nil {
		t.Fatalf("Failed to unmarshal LogEventParams: %v", err)
	}

	if unmarshaled.ID != params.ID {
		t.Errorf("Expected ID %d, got %d", params.ID, unmarshaled.ID)
	}

	if *unmarshaled.UserID != *params.UserID {
		t.Errorf("Expected UserID '%s', got '%s'", *params.UserID, *unmarshaled.UserID)
	}
}

func TestUserAccessLogCreation(t *testing.T) {
	userID := "user-abc"
	now := time.Now()

	accessLog := UserAccessLog{
		ID:            100,
		UserID:        &userID,
		ParticipantID: nil,
		ActivityType:  20,
		IPAddress:     "203.0.113.1",
		FieldUpdated:  "login",
		Time:          now,
	}

	if accessLog.ParticipantID != nil {
		t.Error("Expected ParticipantID to be nil")
	}

	if accessLog.FieldUpdated != "login" {
		t.Errorf("Expected FieldUpdated 'login', got '%v'", accessLog.FieldUpdated)
	}

	if accessLog.Time.IsZero() {
		t.Error("Expected Time to be set")
	}

	if accessLog.ID != 100 {
		t.Errorf("Expected ID 100, got %d", accessLog.ID)
	}

	if *accessLog.UserID != "user-abc" {
		t.Errorf("Expected UserID 'user-abc', got '%s'", *accessLog.UserID)
	}

	if accessLog.ActivityType != 20 {
		t.Errorf("Expected ActivityType 20, got %d", accessLog.ActivityType)
	}

	if accessLog.IPAddress != "203.0.113.1" {
		t.Errorf("Expected IPAddress '203.0.113.1', got '%s'", accessLog.IPAddress)
	}
}

func TestUserAccessLogTimeNotNullable(t *testing.T) {
	now := time.Now()

	accessLog := UserAccessLog{
		ID:        1,
		IPAddress: Localhost,
		Time:      now,
	}

	if accessLog.ID != 1 {
		t.Errorf("Expected ID 1, got %d", accessLog.ID)
	}

	if accessLog.IPAddress != Localhost {
		t.Errorf("Expected IPAddress '127.0.0.1', got '%s'", accessLog.IPAddress)
	}

	// Time should always have a value (not pointer in UserAccessLog)
	if accessLog.Time.IsZero() {
		t.Error("Expected Time to be set, got zero value")
	}

	if !accessLog.Time.Equal(now) {
		t.Error("Expected Time to match the set value")
	}
}

func TestUserAccessLogActivityTypes(t *testing.T) {
	testCases := []struct {
		name         string
		activityType int
	}{
		{"Login", 1},
		{"Logout", 2},
		{"Create", 3},
		{"Update", 4},
		{"Delete", 5},
		{"View", 6},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			accessLog := UserAccessLog{
				ActivityType: tc.activityType,
			}

			if accessLog.ActivityType != tc.activityType {
				t.Errorf("Expected ActivityType %d, got %d", tc.activityType, accessLog.ActivityType)
			}
		})
	}
}

func TestUserAccessLogIPAddressValidation(t *testing.T) {
	testCases := []struct {
		name      string
		ipAddress string
	}{
		{"IPv4", "192.168.1.1"},
		{"IPv4 Loopback", Localhost},
		{"IPv4 Private", LocalNetwork},
		{"IPv6", "2001:0db8:85a3:0000:0000:8a2e:0370:7334"},
		{"IPv6 Loopback", "::1"},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			accessLog := UserAccessLog{
				IPAddress: tc.ipAddress,
			}

			if accessLog.IPAddress != tc.ipAddress {
				t.Errorf("Expected IPAddress '%s', got '%s'", tc.ipAddress, accessLog.IPAddress)
			}
		})
	}
}

func TestUserAccessLogJSONMarshaling(t *testing.T) {
	userID := "user-json-test"
	now := time.Now()

	accessLog := UserAccessLog{
		ID:           50,
		UserID:       &userID,
		ActivityType: 10,
		IPAddress:    "192.0.2.1",
		FieldUpdated: map[string]string{"action": "test"},
		Time:         now,
	}

	jsonData, err := json.Marshal(accessLog)
	if err != nil {
		t.Fatalf("Failed to marshal UserAccessLog: %v", err)
	}

	var unmarshaled UserAccessLog
	err = json.Unmarshal(jsonData, &unmarshaled)
	if err != nil {
		t.Fatalf("Failed to unmarshal UserAccessLog: %v", err)
	}

	if unmarshaled.ID != accessLog.ID {
		t.Errorf("Expected ID %d, got %d", accessLog.ID, unmarshaled.ID)
	}

	if *unmarshaled.UserID != *accessLog.UserID {
		t.Errorf("Expected UserID '%s', got '%s'", *accessLog.UserID, *unmarshaled.UserID)
	}
}

func TestLogEventParamsErrorMessage(t *testing.T) {
	params := LogEventParams{
		ErrorMessage: "Database connection failed",
	}

	if params.ErrorMessage != "Database connection failed" {
		t.Errorf("Expected ErrorMessage 'Database connection failed', got '%s'", params.ErrorMessage)
	}

	// Empty error message
	params2 := LogEventParams{
		ErrorMessage: "",
	}

	if params2.ErrorMessage != "" {
		t.Errorf("Expected empty ErrorMessage, got '%s'", params2.ErrorMessage)
	}
}
