package ucca import ( "time" "github.com/google/uuid" ) // EscalationLevel represents the escalation level (E0-E3). type EscalationLevel string const ( EscalationLevelE0 EscalationLevel = "E0" // Auto-Approve EscalationLevelE1 EscalationLevel = "E1" // Team-Lead Review EscalationLevelE2 EscalationLevel = "E2" // DSB Consultation EscalationLevelE3 EscalationLevel = "E3" // DSB + Legal Review ) // EscalationStatus represents the status of an escalation. type EscalationStatus string const ( EscalationStatusPending EscalationStatus = "pending" EscalationStatusAssigned EscalationStatus = "assigned" EscalationStatusInReview EscalationStatus = "in_review" EscalationStatusApproved EscalationStatus = "approved" EscalationStatusRejected EscalationStatus = "rejected" EscalationStatusReturned EscalationStatus = "returned" ) // EscalationDecision represents the decision made on an escalation. type EscalationDecision string const ( EscalationDecisionApprove EscalationDecision = "approve" EscalationDecisionReject EscalationDecision = "reject" EscalationDecisionModify EscalationDecision = "modify" EscalationDecisionEscalate EscalationDecision = "escalate" ) // Escalation represents an escalation record for a UCCA assessment. type Escalation struct { ID uuid.UUID `json:"id" db:"id"` TenantID uuid.UUID `json:"tenant_id" db:"tenant_id"` AssessmentID uuid.UUID `json:"assessment_id" db:"assessment_id"` EscalationLevel EscalationLevel `json:"escalation_level" db:"escalation_level"` EscalationReason string `json:"escalation_reason" db:"escalation_reason"` AssignedTo *uuid.UUID `json:"assigned_to,omitempty" db:"assigned_to"` AssignedRole *string `json:"assigned_role,omitempty" db:"assigned_role"` AssignedAt *time.Time `json:"assigned_at,omitempty" db:"assigned_at"` Status EscalationStatus `json:"status" db:"status"` ReviewerID *uuid.UUID `json:"reviewer_id,omitempty" db:"reviewer_id"` ReviewerNotes *string `json:"reviewer_notes,omitempty" db:"reviewer_notes"` ReviewedAt *time.Time `json:"reviewed_at,omitempty" db:"reviewed_at"` Decision *EscalationDecision `json:"decision,omitempty" db:"decision"` DecisionNotes *string `json:"decision_notes,omitempty" db:"decision_notes"` DecisionAt *time.Time `json:"decision_at,omitempty" db:"decision_at"` Conditions []string `json:"conditions" db:"conditions"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` DueDate *time.Time `json:"due_date,omitempty" db:"due_date"` NotificationSent bool `json:"notification_sent" db:"notification_sent"` NotificationSentAt *time.Time `json:"notification_sent_at,omitempty" db:"notification_sent_at"` } // EscalationHistory represents an audit trail entry for escalation changes. type EscalationHistory struct { ID uuid.UUID `json:"id" db:"id"` EscalationID uuid.UUID `json:"escalation_id" db:"escalation_id"` Action string `json:"action" db:"action"` OldStatus string `json:"old_status,omitempty" db:"old_status"` NewStatus string `json:"new_status,omitempty" db:"new_status"` OldLevel string `json:"old_level,omitempty" db:"old_level"` NewLevel string `json:"new_level,omitempty" db:"new_level"` ActorID uuid.UUID `json:"actor_id" db:"actor_id"` ActorRole string `json:"actor_role,omitempty" db:"actor_role"` Notes string `json:"notes,omitempty" db:"notes"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // DSBPoolMember represents a member of the DSB review pool. type DSBPoolMember struct { ID uuid.UUID `json:"id" db:"id"` TenantID uuid.UUID `json:"tenant_id" db:"tenant_id"` UserID uuid.UUID `json:"user_id" db:"user_id"` UserName string `json:"user_name" db:"user_name"` UserEmail string `json:"user_email" db:"user_email"` Role string `json:"role" db:"role"` IsActive bool `json:"is_active" db:"is_active"` MaxConcurrentReviews int `json:"max_concurrent_reviews" db:"max_concurrent_reviews"` CurrentReviews int `json:"current_reviews" db:"current_reviews"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // EscalationSLA represents SLA configuration for an escalation level. type EscalationSLA struct { ID uuid.UUID `json:"id" db:"id"` TenantID uuid.UUID `json:"tenant_id" db:"tenant_id"` EscalationLevel EscalationLevel `json:"escalation_level" db:"escalation_level"` ResponseHours int `json:"response_hours" db:"response_hours"` ResolutionHours int `json:"resolution_hours" db:"resolution_hours"` NotifyOnCreation bool `json:"notify_on_creation" db:"notify_on_creation"` NotifyOnApproachingSLA bool `json:"notify_on_approaching_sla" db:"notify_on_approaching_sla"` NotifyOnSLABreach bool `json:"notify_on_sla_breach" db:"notify_on_sla_breach"` ApproachingSLAHours int `json:"approaching_sla_hours" db:"approaching_sla_hours"` AutoEscalateOnBreach bool `json:"auto_escalate_on_breach" db:"auto_escalate_on_breach"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // EscalationWithAssessment combines escalation with assessment summary. type EscalationWithAssessment struct { Escalation AssessmentTitle string `json:"assessment_title"` AssessmentFeasibility string `json:"assessment_feasibility"` AssessmentRiskScore int `json:"assessment_risk_score"` AssessmentDomain string `json:"assessment_domain"` } // EscalationStats provides statistics for escalations. type EscalationStats struct { TotalPending int `json:"total_pending"` TotalInReview int `json:"total_in_review"` TotalApproved int `json:"total_approved"` TotalRejected int `json:"total_rejected"` ByLevel map[EscalationLevel]int `json:"by_level"` OverdueSLA int `json:"overdue_sla"` ApproachingSLA int `json:"approaching_sla"` AvgResolutionHours float64 `json:"avg_resolution_hours"` } // EscalationTrigger contains the logic to determine escalation level. type EscalationTrigger struct { // Thresholds E1RiskThreshold int // Risk score threshold for E1 (default: 20) E2RiskThreshold int // Risk score threshold for E2 (default: 40) E3RiskThreshold int // Risk score threshold for E3 (default: 60) } // DefaultEscalationTrigger returns the default escalation trigger configuration. func DefaultEscalationTrigger() *EscalationTrigger { return &EscalationTrigger{ E1RiskThreshold: 20, E2RiskThreshold: 40, E3RiskThreshold: 60, } } // DetermineEscalationLevel determines the appropriate escalation level for an assessment. func (t *EscalationTrigger) DetermineEscalationLevel(result *AssessmentResult) (EscalationLevel, string) { reasons := []string{} // E3: Highest priority checks // - BLOCK rules triggered // - Risk > 60 // - Art. 22 risk (automated individual decisions) hasBlock := false for _, rule := range result.TriggeredRules { if rule.Severity == "BLOCK" { hasBlock = true reasons = append(reasons, "BLOCK-Regel ausgelöst: "+rule.Code) break } } if hasBlock || result.RiskScore > t.E3RiskThreshold || result.Art22Risk { if result.RiskScore > t.E3RiskThreshold { reasons = append(reasons, "Risiko-Score über 60") } if result.Art22Risk { reasons = append(reasons, "Art. 22 DSGVO Risiko (automatisierte Entscheidungen)") } return EscalationLevelE3, joinReasons(reasons, "E3 erforderlich: ") } // E2: Medium priority checks // - Art. 9 data (special categories) // - DSFA recommended // - Risk 40-60 hasArt9 := false for _, rule := range result.TriggeredRules { if rule.Code == "R-002" || rule.Code == "A-002" { // Art. 9 rules hasArt9 = true reasons = append(reasons, "Besondere Datenkategorien (Art. 9 DSGVO)") break } } if hasArt9 || result.DSFARecommended || result.RiskScore > t.E2RiskThreshold { if result.DSFARecommended { reasons = append(reasons, "DSFA empfohlen") } if result.RiskScore > t.E2RiskThreshold { reasons = append(reasons, "Risiko-Score 40-60") } return EscalationLevelE2, joinReasons(reasons, "DSB-Konsultation erforderlich: ") } // E1: Low priority checks // - WARN rules triggered // - Risk 20-40 hasWarn := false for _, rule := range result.TriggeredRules { if rule.Severity == "WARN" { hasWarn = true reasons = append(reasons, "WARN-Regel ausgelöst") break } } if hasWarn || result.RiskScore > t.E1RiskThreshold { if result.RiskScore > t.E1RiskThreshold { reasons = append(reasons, "Risiko-Score 20-40") } return EscalationLevelE1, joinReasons(reasons, "Team-Lead Review erforderlich: ") } // E0: Auto-approve // - Only INFO rules // - Risk < 20 return EscalationLevelE0, "Automatische Freigabe: Nur INFO-Regeln, niedriges Risiko" } // GetDefaultSLA returns the default SLA for an escalation level. func GetDefaultSLA(level EscalationLevel) (responseHours, resolutionHours int) { switch level { case EscalationLevelE0: return 0, 0 // Auto-approve, no SLA case EscalationLevelE1: return 24, 72 // 1 day response, 3 days resolution case EscalationLevelE2: return 8, 48 // 8 hours response, 2 days resolution case EscalationLevelE3: return 4, 24 // 4 hours response, 1 day resolution (urgent) default: return 24, 72 } } // GetRoleForLevel returns the required role for reviewing an escalation level. func GetRoleForLevel(level EscalationLevel) string { switch level { case EscalationLevelE0: return "" // No review needed case EscalationLevelE1: return "team_lead" case EscalationLevelE2: return "dsb" case EscalationLevelE3: return "dsb" // DSB + Legal, but DSB is primary default: return "dsb" } } func joinReasons(reasons []string, prefix string) string { if len(reasons) == 0 { return prefix } result := prefix for i, r := range reasons { if i > 0 { result += "; " } result += r } return result } // CreateEscalationRequest is the request to create an escalation. type CreateEscalationRequest struct { AssessmentID uuid.UUID `json:"assessment_id" binding:"required"` } // AssignEscalationRequest is the request to assign an escalation. type AssignEscalationRequest struct { AssignedTo uuid.UUID `json:"assigned_to" binding:"required"` } // DecideEscalationRequest is the request to make a decision on an escalation. type DecideEscalationRequest struct { Decision EscalationDecision `json:"decision" binding:"required"` DecisionNotes string `json:"decision_notes"` Conditions []string `json:"conditions,omitempty"` }