package services

import (
	"testing"
	"time"

	"github.com/breakpilot/consent-service/internal/models"
)

// TestDSRRequestTypeLabel tests label generation for request types
func TestDSRRequestTypeLabel(t *testing.T) {
	tests := []struct {
		name     string
		reqType  models.DSRRequestType
		expected string
	}{
		{"access type", models.DSRTypeAccess, "Auskunftsanfrage (Art. 15)"},
		{"rectification type", models.DSRTypeRectification, "Berichtigungsanfrage (Art. 16)"},
		{"erasure type", models.DSRTypeErasure, "Löschanfrage (Art. 17)"},
		{"restriction type", models.DSRTypeRestriction, "Einschränkungsanfrage (Art. 18)"},
		{"portability type", models.DSRTypePortability, "Datenübertragung (Art. 20)"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := tt.reqType.Label()
			if result != tt.expected {
				t.Errorf("Expected %s, got %s", tt.expected, result)
			}
		})
	}
}

// TestDSRRequestTypeDeadlineDays tests deadline calculation for different request types
func TestDSRRequestTypeDeadlineDays(t *testing.T) {
	tests := []struct {
		name         string
		reqType      models.DSRRequestType
		expectedDays int
	}{
		{"access has 30 days", models.DSRTypeAccess, 30},
		{"portability has 30 days", models.DSRTypePortability, 30},
		{"rectification has 14 days", models.DSRTypeRectification, 14},
		{"erasure has 14 days", models.DSRTypeErasure, 14},
		{"restriction has 14 days", models.DSRTypeRestriction, 14},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := tt.reqType.DeadlineDays()
			if result != tt.expectedDays {
				t.Errorf("Expected %d days, got %d", tt.expectedDays, result)
			}
		})
	}
}

// TestDSRRequestTypeIsExpedited tests expedited flag for request types
func TestDSRRequestTypeIsExpedited(t *testing.T) {
	tests := []struct {
		name        string
		reqType     models.DSRRequestType
		isExpedited bool
	}{
		{"access not expedited", models.DSRTypeAccess, false},
		{"portability not expedited", models.DSRTypePortability, false},
		{"rectification is expedited", models.DSRTypeRectification, true},
		{"erasure is expedited", models.DSRTypeErasure, true},
		{"restriction is expedited", models.DSRTypeRestriction, true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := tt.reqType.IsExpedited()
			if result != tt.isExpedited {
				t.Errorf("Expected IsExpedited=%v, got %v", tt.isExpedited, result)
			}
		})
	}
}

// TestDSRStatusLabel tests label generation for statuses
func TestDSRStatusLabel(t *testing.T) {
	tests := []struct {
		name     string
		status   models.DSRStatus
		expected string
	}{
		{"intake status", models.DSRStatusIntake, "Eingang"},
		{"identity verification", models.DSRStatusIdentityVerification, "Identitätsprüfung"},
		{"processing status", models.DSRStatusProcessing, "In Bearbeitung"},
		{"completed status", models.DSRStatusCompleted, "Abgeschlossen"},
		{"rejected status", models.DSRStatusRejected, "Abgelehnt"},
		{"cancelled status", models.DSRStatusCancelled, "Storniert"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := tt.status.Label()
			if result != tt.expected {
				t.Errorf("Expected %s, got %s", tt.expected, result)
			}
		})
	}
}

// TestValidDSRRequestType tests request type validation
func TestValidDSRRequestType(t *testing.T) {
	tests := []struct {
		name    string
		reqType string
		valid   bool
	}{
		{"valid access", "access", true},
		{"valid rectification", "rectification", true},
		{"valid erasure", "erasure", true},
		{"valid restriction", "restriction", true},
		{"valid portability", "portability", true},
		{"invalid type", "invalid", false},
		{"empty type", "", false},
		{"random string", "delete_everything", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := models.IsValidDSRRequestType(tt.reqType)
			if result != tt.valid {
				t.Errorf("Expected IsValidDSRRequestType=%v for %s, got %v", tt.valid, tt.reqType, result)
			}
		})
	}
}

// TestValidDSRStatus tests status validation
func TestValidDSRStatus(t *testing.T) {
	tests := []struct {
		name   string
		status string
		valid  bool
	}{
		{"valid intake", "intake", true},
		{"valid identity_verification", "identity_verification", true},
		{"valid processing", "processing", true},
		{"valid completed", "completed", true},
		{"valid rejected", "rejected", true},
		{"valid cancelled", "cancelled", true},
		{"invalid status", "invalid", false},
		{"empty status", "", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := models.IsValidDSRStatus(tt.status)
			if result != tt.valid {
				t.Errorf("Expected IsValidDSRStatus=%v for %s, got %v", tt.valid, tt.status, result)
			}
		})
	}
}

// TestDSRStatusTransitionValidation tests allowed status transitions
func TestDSRStatusTransitionValidation(t *testing.T) {
	tests := []struct {
		name       string
		fromStatus models.DSRStatus
		toStatus   models.DSRStatus
		allowed    bool
	}{
		// From intake
		{"intake to identity_verification", models.DSRStatusIntake, models.DSRStatusIdentityVerification, true},
		{"intake to processing", models.DSRStatusIntake, models.DSRStatusProcessing, true},
		{"intake to rejected", models.DSRStatusIntake, models.DSRStatusRejected, true},
		{"intake to cancelled", models.DSRStatusIntake, models.DSRStatusCancelled, true},
		{"intake to completed invalid", models.DSRStatusIntake, models.DSRStatusCompleted, false},

		// From identity_verification
		{"identity to processing", models.DSRStatusIdentityVerification, models.DSRStatusProcessing, true},
		{"identity to rejected", models.DSRStatusIdentityVerification, models.DSRStatusRejected, true},
		{"identity to cancelled", models.DSRStatusIdentityVerification, models.DSRStatusCancelled, true},

		// From processing
		{"processing to completed", models.DSRStatusProcessing, models.DSRStatusCompleted, true},
		{"processing to rejected", models.DSRStatusProcessing, models.DSRStatusRejected, true},
		{"processing to intake invalid", models.DSRStatusProcessing, models.DSRStatusIntake, false},

		// From completed
		{"completed to anything invalid", models.DSRStatusCompleted, models.DSRStatusProcessing, false},

		// From rejected
		{"rejected to anything invalid", models.DSRStatusRejected, models.DSRStatusProcessing, false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := testIsValidStatusTransition(tt.fromStatus, tt.toStatus)
			if result != tt.allowed {
				t.Errorf("Expected transition %s->%s allowed=%v, got %v",
					tt.fromStatus, tt.toStatus, tt.allowed, result)
			}
		})
	}
}

// testIsValidStatusTransition is a test helper for validating status transitions
// This mirrors the logic in dsr_service.go for testing purposes
func testIsValidStatusTransition(from, to models.DSRStatus) bool {
	validTransitions := map[models.DSRStatus][]models.DSRStatus{
		models.DSRStatusIntake: {
			models.DSRStatusIdentityVerification,
			models.DSRStatusProcessing,
			models.DSRStatusRejected,
			models.DSRStatusCancelled,
		},
		models.DSRStatusIdentityVerification: {
			models.DSRStatusProcessing,
			models.DSRStatusRejected,
			models.DSRStatusCancelled,
		},
		models.DSRStatusProcessing: {
			models.DSRStatusCompleted,
			models.DSRStatusRejected,
			models.DSRStatusCancelled,
		},
		models.DSRStatusCompleted: {},
		models.DSRStatusRejected:  {},
		models.DSRStatusCancelled: {},
	}

	allowed, exists := validTransitions[from]
	if !exists {
		return false
	}

	for _, s := range allowed {
		if s == to {
			return true
		}
	}
	return false
}

// TestCalculateDeadline tests deadline calculation
func TestCalculateDeadline(t *testing.T) {
	tests := []struct {
		name         string
		reqType      models.DSRRequestType
		expectedDays int
	}{
		{"access 30 days", models.DSRTypeAccess, 30},
		{"erasure 14 days", models.DSRTypeErasure, 14},
		{"rectification 14 days", models.DSRTypeRectification, 14},
		{"restriction 14 days", models.DSRTypeRestriction, 14},
		{"portability 30 days", models.DSRTypePortability, 30},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			now := time.Now()
			deadline := now.AddDate(0, 0, tt.expectedDays)
			days := tt.reqType.DeadlineDays()

			if days != tt.expectedDays {
				t.Errorf("Expected %d days, got %d", tt.expectedDays, days)
			}

			// Verify deadline is approximately correct (within 1 day due to test timing)
			calculatedDeadline := now.AddDate(0, 0, days)
			diff := calculatedDeadline.Sub(deadline)
			if diff > time.Hour*24 || diff < -time.Hour*24 {
				t.Errorf("Deadline calculation off by more than a day")
			}
		})
	}
}

// TestCreateDSRRequest_Validation tests validation of create request
func TestCreateDSRRequest_Validation(t *testing.T) {
	tests := []struct {
		name        string
		request     models.CreateDSRRequest
		expectError bool
	}{
		{
			name: "valid access request",
			request: models.CreateDSRRequest{
				RequestType:    "access",
				RequesterEmail: "test@example.com",
			},
			expectError: false,
		},
		{
			name: "valid erasure request with name",
			request: models.CreateDSRRequest{
				RequestType:    "erasure",
				RequesterEmail: "test@example.com",
				RequesterName:  stringPtr("Max Mustermann"),
			},
			expectError: false,
		},
		{
			name: "missing email",
			request: models.CreateDSRRequest{
				RequestType: "access",
			},
			expectError: true,
		},
		{
			name: "invalid request type",
			request: models.CreateDSRRequest{
				RequestType:    "invalid_type",
				RequesterEmail: "test@example.com",
			},
			expectError: true,
		},
		{
			name: "empty request type",
			request: models.CreateDSRRequest{
				RequestType:    "",
				RequesterEmail: "test@example.com",
			},
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := testValidateCreateDSRRequest(tt.request)
			hasError := err != nil

			if hasError != tt.expectError {
				t.Errorf("Expected error=%v, got error=%v (err: %v)", tt.expectError, hasError, err)
			}
		})
	}
}

// testValidateCreateDSRRequest is a test helper for validating create DSR requests
func testValidateCreateDSRRequest(req models.CreateDSRRequest) error {
	if req.RequesterEmail == "" {
		return &dsrValidationError{"requester_email is required"}
	}
	if !models.IsValidDSRRequestType(req.RequestType) {
		return &dsrValidationError{"invalid request_type"}
	}
	return nil
}

type dsrValidationError struct {
	Message string
}

func (e *dsrValidationError) Error() string {
	return e.Message
}

// TestDSRTemplateTypes tests the template types
func TestDSRTemplateTypes(t *testing.T) {
	expectedTemplates := []string{
		"dsr_receipt_access",
		"dsr_receipt_rectification",
		"dsr_receipt_erasure",
		"dsr_receipt_restriction",
		"dsr_receipt_portability",
		"dsr_identity_request",
		"dsr_processing_started",
		"dsr_processing_update",
		"dsr_clarification_request",
		"dsr_completed_access",
		"dsr_completed_rectification",
		"dsr_completed_erasure",
		"dsr_completed_restriction",
		"dsr_completed_portability",
		"dsr_restriction_lifted",
		"dsr_rejected_identity",
		"dsr_rejected_exception",
		"dsr_rejected_unfounded",
		"dsr_deadline_warning",
	}

	// This test documents the expected template types
	// The actual templates are created in database migration
	for _, template := range expectedTemplates {
		if template == "" {
			t.Error("Template type should not be empty")
		}
	}

	if len(expectedTemplates) != 19 {
		t.Errorf("Expected 19 template types, got %d", len(expectedTemplates))
	}
}

// TestErasureExceptionTypes tests Art. 17(3) exception types
func TestErasureExceptionTypes(t *testing.T) {
	exceptions := []struct {
		code        string
		description string
	}{
		{"art_17_3_a", "Meinungs- und Informationsfreiheit"},
		{"art_17_3_b", "Rechtliche Verpflichtung"},
		{"art_17_3_c", "Öffentliches Interesse im Gesundheitsbereich"},
		{"art_17_3_d", "Archivzwecke, wissenschaftliche/historische Forschung"},
		{"art_17_3_e", "Geltendmachung, Ausübung oder Verteidigung von Rechtsansprüchen"},
	}

	if len(exceptions) != 5 {
		t.Errorf("Expected 5 Art. 17(3) exceptions, got %d", len(exceptions))
	}

	for _, ex := range exceptions {
		if ex.code == "" || ex.description == "" {
			t.Error("Exception code and description should not be empty")
		}
	}
}

// stringPtr returns a pointer to the given string
func stringPtr(s string) *string {
	return &s
}
