package handlers

import (
	"fmt"
	"net/http"
	"time"

	"github.com/breakpilot/ai-compliance-sdk/internal/incidents"
	"github.com/breakpilot/ai-compliance-sdk/internal/rbac"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

// IncidentHandlers handles incident/breach management HTTP requests
type IncidentHandlers struct {
	store *incidents.Store
}

// NewIncidentHandlers creates new incident handlers
func NewIncidentHandlers(store *incidents.Store) *IncidentHandlers {
	return &IncidentHandlers{store: store}
}

// ============================================================================
// Incident CRUD
// ============================================================================

// CreateIncident creates a new incident
// POST /sdk/v1/incidents
func (h *IncidentHandlers) CreateIncident(c *gin.Context) {
	var req incidents.CreateIncidentRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	tenantID := rbac.GetTenantID(c)
	userID := rbac.GetUserID(c)

	detectedAt := time.Now().UTC()
	if req.DetectedAt != nil {
		detectedAt = *req.DetectedAt
	}

	// Auto-calculate 72h deadline per DSGVO Art. 33
	deadline := incidents.Calculate72hDeadline(detectedAt)

	incident := &incidents.Incident{
		TenantID:                 tenantID,
		Title:                    req.Title,
		Description:              req.Description,
		Category:                 req.Category,
		Status:                   incidents.IncidentStatusDetected,
		Severity:                 req.Severity,
		DetectedAt:               detectedAt,
		ReportedBy:               userID,
		AffectedDataCategories:   req.AffectedDataCategories,
		AffectedDataSubjectCount: req.AffectedDataSubjectCount,
		AffectedSystems:          req.AffectedSystems,
		AuthorityNotification: &incidents.AuthorityNotification{
			Status:   incidents.NotificationStatusPending,
			Deadline: deadline,
		},
		DataSubjectNotification: &incidents.DataSubjectNotification{
			Required: false,
			Status:   incidents.NotificationStatusNotRequired,
		},
		Timeline: []incidents.TimelineEntry{
			{
				Timestamp: time.Now().UTC(),
				Action:    "incident_created",
				UserID:    userID,
				Details:   "Incident detected and reported",
			},
		},
	}

	if err := h.store.CreateIncident(c.Request.Context(), incident); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, gin.H{
		"incident":               incident,
		"authority_deadline":     deadline,
		"hours_until_deadline":   time.Until(deadline).Hours(),
	})
}

// GetIncident retrieves an incident by ID
// GET /sdk/v1/incidents/:id
func (h *IncidentHandlers) GetIncident(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid incident ID"})
		return
	}

	incident, err := h.store.GetIncident(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if incident == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "incident not found"})
		return
	}

	// Get measures
	measures, _ := h.store.ListMeasures(c.Request.Context(), id)

	// Calculate deadline info if authority notification exists
	var deadlineInfo gin.H
	if incident.AuthorityNotification != nil {
		hoursRemaining := time.Until(incident.AuthorityNotification.Deadline).Hours()
		deadlineInfo = gin.H{
			"deadline":        incident.AuthorityNotification.Deadline,
			"hours_remaining": hoursRemaining,
			"overdue":         hoursRemaining < 0,
		}
	}

	c.JSON(http.StatusOK, gin.H{
		"incident":      incident,
		"measures":      measures,
		"deadline_info": deadlineInfo,
	})
}

// ListIncidents lists incidents for a tenant
// GET /sdk/v1/incidents
func (h *IncidentHandlers) ListIncidents(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)

	filters := &incidents.IncidentFilters{
		Limit: 50,
	}

	if status := c.Query("status"); status != "" {
		filters.Status = incidents.IncidentStatus(status)
	}
	if severity := c.Query("severity"); severity != "" {
		filters.Severity = incidents.IncidentSeverity(severity)
	}
	if category := c.Query("category"); category != "" {
		filters.Category = incidents.IncidentCategory(category)
	}

	incidentList, total, err := h.store.ListIncidents(c.Request.Context(), tenantID, filters)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, incidents.IncidentListResponse{
		Incidents: incidentList,
		Total:     total,
	})
}

// UpdateIncident updates an incident
// PUT /sdk/v1/incidents/:id
func (h *IncidentHandlers) UpdateIncident(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid incident ID"})
		return
	}

	incident, err := h.store.GetIncident(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if incident == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "incident not found"})
		return
	}

	var req incidents.UpdateIncidentRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	if req.Title != "" {
		incident.Title = req.Title
	}
	if req.Description != "" {
		incident.Description = req.Description
	}
	if req.Category != "" {
		incident.Category = req.Category
	}
	if req.Status != "" {
		incident.Status = req.Status
	}
	if req.Severity != "" {
		incident.Severity = req.Severity
	}
	if req.AffectedDataCategories != nil {
		incident.AffectedDataCategories = req.AffectedDataCategories
	}
	if req.AffectedDataSubjectCount != nil {
		incident.AffectedDataSubjectCount = *req.AffectedDataSubjectCount
	}
	if req.AffectedSystems != nil {
		incident.AffectedSystems = req.AffectedSystems
	}

	if err := h.store.UpdateIncident(c.Request.Context(), incident); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"incident": incident})
}

// DeleteIncident deletes an incident
// DELETE /sdk/v1/incidents/:id
func (h *IncidentHandlers) DeleteIncident(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid incident ID"})
		return
	}

	if err := h.store.DeleteIncident(c.Request.Context(), id); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "incident deleted"})
}

// ============================================================================
// Risk Assessment
// ============================================================================

// AssessRisk performs a risk assessment for an incident
// POST /sdk/v1/incidents/:id/risk-assessment
func (h *IncidentHandlers) AssessRisk(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid incident ID"})
		return
	}

	incident, err := h.store.GetIncident(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if incident == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "incident not found"})
		return
	}

	var req incidents.RiskAssessmentRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	userID := rbac.GetUserID(c)

	// Auto-calculate risk level
	riskLevel := incidents.CalculateRiskLevel(req.Likelihood, req.Impact)
	notificationRequired := incidents.IsNotificationRequired(riskLevel)

	assessment := &incidents.RiskAssessment{
		Likelihood: req.Likelihood,
		Impact:     req.Impact,
		RiskLevel:  riskLevel,
		AssessedAt: time.Now().UTC(),
		AssessedBy: userID,
		Notes:      req.Notes,
	}

	if err := h.store.UpdateRiskAssessment(c.Request.Context(), id, assessment); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// Update status to assessment
	incident.Status = incidents.IncidentStatusAssessment
	h.store.UpdateIncident(c.Request.Context(), incident)

	// Add timeline entry
	h.store.AddTimelineEntry(c.Request.Context(), id, incidents.TimelineEntry{
		Timestamp: time.Now().UTC(),
		Action:    "risk_assessed",
		UserID:    userID,
		Details:   fmt.Sprintf("Risk level: %s (likelihood=%d, impact=%d)", riskLevel, req.Likelihood, req.Impact),
	})

	// If notification is required, update authority notification status
	if notificationRequired && incident.AuthorityNotification != nil {
		incident.AuthorityNotification.Status = incidents.NotificationStatusPending
		h.store.UpdateAuthorityNotification(c.Request.Context(), id, incident.AuthorityNotification)

		// Update status to notification_required
		incident.Status = incidents.IncidentStatusNotificationRequired
		h.store.UpdateIncident(c.Request.Context(), incident)
	}

	c.JSON(http.StatusOK, gin.H{
		"risk_assessment":       assessment,
		"notification_required": notificationRequired,
		"incident_status":       incident.Status,
	})
}

// ============================================================================
// Authority Notification (Art. 33)
// ============================================================================

// SubmitAuthorityNotification submits the supervisory authority notification
// POST /sdk/v1/incidents/:id/authority-notification
func (h *IncidentHandlers) SubmitAuthorityNotification(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid incident ID"})
		return
	}

	incident, err := h.store.GetIncident(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if incident == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "incident not found"})
		return
	}

	var req incidents.SubmitAuthorityNotificationRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	userID := rbac.GetUserID(c)
	now := time.Now().UTC()

	// Preserve existing deadline
	deadline := incidents.Calculate72hDeadline(incident.DetectedAt)
	if incident.AuthorityNotification != nil {
		deadline = incident.AuthorityNotification.Deadline
	}

	notification := &incidents.AuthorityNotification{
		Status:          incidents.NotificationStatusSent,
		Deadline:        deadline,
		SubmittedAt:     &now,
		AuthorityName:   req.AuthorityName,
		ReferenceNumber: req.ReferenceNumber,
		ContactPerson:   req.ContactPerson,
		Notes:           req.Notes,
	}

	if err := h.store.UpdateAuthorityNotification(c.Request.Context(), id, notification); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// Update incident status
	incident.Status = incidents.IncidentStatusNotificationSent
	h.store.UpdateIncident(c.Request.Context(), incident)

	// Add timeline entry
	h.store.AddTimelineEntry(c.Request.Context(), id, incidents.TimelineEntry{
		Timestamp: now,
		Action:    "authority_notified",
		UserID:    userID,
		Details:   "Authority notification submitted to " + req.AuthorityName,
	})

	c.JSON(http.StatusOK, gin.H{
		"authority_notification": notification,
		"submitted_within_72h":  now.Before(deadline),
	})
}

// ============================================================================
// Data Subject Notification (Art. 34)
// ============================================================================

// NotifyDataSubjects submits the data subject notification
// POST /sdk/v1/incidents/:id/data-subject-notification
func (h *IncidentHandlers) NotifyDataSubjects(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid incident ID"})
		return
	}

	incident, err := h.store.GetIncident(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if incident == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "incident not found"})
		return
	}

	var req incidents.NotifyDataSubjectsRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	userID := rbac.GetUserID(c)
	now := time.Now().UTC()

	affectedCount := req.AffectedCount
	if affectedCount == 0 {
		affectedCount = incident.AffectedDataSubjectCount
	}

	notification := &incidents.DataSubjectNotification{
		Required:         true,
		Status:           incidents.NotificationStatusSent,
		SentAt:           &now,
		AffectedCount:    affectedCount,
		NotificationText: req.NotificationText,
		Channel:          req.Channel,
	}

	if err := h.store.UpdateDataSubjectNotification(c.Request.Context(), id, notification); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// Add timeline entry
	h.store.AddTimelineEntry(c.Request.Context(), id, incidents.TimelineEntry{
		Timestamp: now,
		Action:    "data_subjects_notified",
		UserID:    userID,
		Details:   "Data subjects notified via " + req.Channel + " (" + fmt.Sprintf("%d", affectedCount) + " affected)",
	})

	c.JSON(http.StatusOK, gin.H{
		"data_subject_notification": notification,
	})
}

// ============================================================================
// Measures
// ============================================================================

// AddMeasure adds a corrective measure to an incident
// POST /sdk/v1/incidents/:id/measures
func (h *IncidentHandlers) AddMeasure(c *gin.Context) {
	incidentID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid incident ID"})
		return
	}

	// Verify incident exists
	incident, err := h.store.GetIncident(c.Request.Context(), incidentID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if incident == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "incident not found"})
		return
	}

	var req incidents.AddMeasureRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	userID := rbac.GetUserID(c)

	measure := &incidents.IncidentMeasure{
		IncidentID:  incidentID,
		Title:       req.Title,
		Description: req.Description,
		MeasureType: req.MeasureType,
		Status:      incidents.MeasureStatusPlanned,
		Responsible: req.Responsible,
		DueDate:     req.DueDate,
	}

	if err := h.store.AddMeasure(c.Request.Context(), measure); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// Add timeline entry
	h.store.AddTimelineEntry(c.Request.Context(), incidentID, incidents.TimelineEntry{
		Timestamp: time.Now().UTC(),
		Action:    "measure_added",
		UserID:    userID,
		Details:   "Measure added: " + req.Title + " (" + string(req.MeasureType) + ")",
	})

	c.JSON(http.StatusCreated, gin.H{"measure": measure})
}

// UpdateMeasure updates a measure
// PUT /sdk/v1/incidents/measures/:measureId
func (h *IncidentHandlers) UpdateMeasure(c *gin.Context) {
	measureID, err := uuid.Parse(c.Param("measureId"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid measure ID"})
		return
	}

	var req struct {
		Title       string               `json:"title,omitempty"`
		Description string               `json:"description,omitempty"`
		MeasureType incidents.MeasureType `json:"measure_type,omitempty"`
		Status      incidents.MeasureStatus `json:"status,omitempty"`
		Responsible string               `json:"responsible,omitempty"`
		DueDate     *time.Time           `json:"due_date,omitempty"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	measure := &incidents.IncidentMeasure{
		ID:          measureID,
		Title:       req.Title,
		Description: req.Description,
		MeasureType: req.MeasureType,
		Status:      req.Status,
		Responsible: req.Responsible,
		DueDate:     req.DueDate,
	}

	if err := h.store.UpdateMeasure(c.Request.Context(), measure); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"measure": measure})
}

// CompleteMeasure marks a measure as completed
// POST /sdk/v1/incidents/measures/:measureId/complete
func (h *IncidentHandlers) CompleteMeasure(c *gin.Context) {
	measureID, err := uuid.Parse(c.Param("measureId"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid measure ID"})
		return
	}

	if err := h.store.CompleteMeasure(c.Request.Context(), measureID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "measure completed"})
}

// ============================================================================
// Timeline
// ============================================================================

// AddTimelineEntry adds a timeline entry to an incident
// POST /sdk/v1/incidents/:id/timeline
func (h *IncidentHandlers) AddTimelineEntry(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid incident ID"})
		return
	}

	var req incidents.AddTimelineEntryRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	userID := rbac.GetUserID(c)

	entry := incidents.TimelineEntry{
		Timestamp: time.Now().UTC(),
		Action:    req.Action,
		UserID:    userID,
		Details:   req.Details,
	}

	if err := h.store.AddTimelineEntry(c.Request.Context(), id, entry); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, gin.H{"timeline_entry": entry})
}

// ============================================================================
// Close Incident
// ============================================================================

// CloseIncident closes an incident with root cause analysis
// POST /sdk/v1/incidents/:id/close
func (h *IncidentHandlers) CloseIncident(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid incident ID"})
		return
	}

	incident, err := h.store.GetIncident(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if incident == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "incident not found"})
		return
	}

	var req incidents.CloseIncidentRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	userID := rbac.GetUserID(c)

	if err := h.store.CloseIncident(c.Request.Context(), id, req.RootCause, req.LessonsLearned); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// Add timeline entry
	h.store.AddTimelineEntry(c.Request.Context(), id, incidents.TimelineEntry{
		Timestamp: time.Now().UTC(),
		Action:    "incident_closed",
		UserID:    userID,
		Details:   "Incident closed. Root cause: " + req.RootCause,
	})

	c.JSON(http.StatusOK, gin.H{
		"message":         "incident closed",
		"root_cause":      req.RootCause,
		"lessons_learned": req.LessonsLearned,
	})
}

// ============================================================================
// Statistics
// ============================================================================

// GetStatistics returns aggregated incident statistics
// GET /sdk/v1/incidents/statistics
func (h *IncidentHandlers) GetStatistics(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)

	stats, err := h.store.GetStatistics(c.Request.Context(), tenantID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, stats)
}

