package services

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

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

// ExtendDeadline extends the deadline for a DSR
func (s *DSRService) ExtendDeadline(ctx context.Context, id uuid.UUID, reason string, days int, extendedBy uuid.UUID) error {
	// Default extension is 2 months (60 days) per Art. 12(3)
	if days <= 0 {
		days = 60
	}

	_, err := s.pool.Exec(ctx, `
		UPDATE data_subject_requests
		SET extended_deadline_at = deadline_at + ($1 || ' days')::INTERVAL,
			extension_reason = $2,
			updated_at = NOW()
		WHERE id = $3
	`, days, reason, id)
	if err != nil {
		return fmt.Errorf("failed to extend deadline: %w", err)
	}

	s.recordStatusChange(ctx, id, nil, "", &extendedBy, fmt.Sprintf("Frist um %d Tage verlängert: %s", days, reason))

	return nil
}

// GetDashboardStats returns statistics for the admin dashboard
func (s *DSRService) GetDashboardStats(ctx context.Context) (*models.DSRDashboardStats, error) {
	stats := &models.DSRDashboardStats{
		ByType:   make(map[string]int),
		ByStatus: make(map[string]int),
	}

	// Total requests
	s.pool.QueryRow(ctx, "SELECT COUNT(*) FROM data_subject_requests").Scan(&stats.TotalRequests)

	// Pending requests (not completed, rejected, or cancelled)
	s.pool.QueryRow(ctx, `
		SELECT COUNT(*) FROM data_subject_requests
		WHERE status NOT IN ('completed', 'rejected', 'cancelled')
	`).Scan(&stats.PendingRequests)

	// Overdue requests
	s.pool.QueryRow(ctx, `
		SELECT COUNT(*) FROM data_subject_requests
		WHERE COALESCE(extended_deadline_at, deadline_at) < NOW()
		AND status NOT IN ('completed', 'rejected', 'cancelled')
	`).Scan(&stats.OverdueRequests)

	// Completed this month
	s.pool.QueryRow(ctx, `
		SELECT COUNT(*) FROM data_subject_requests
		WHERE status = 'completed'
		AND completed_at >= DATE_TRUNC('month', NOW())
	`).Scan(&stats.CompletedThisMonth)

	// Average processing days
	s.pool.QueryRow(ctx, `
		SELECT COALESCE(AVG(EXTRACT(EPOCH FROM (completed_at - created_at)) / 86400), 0)
		FROM data_subject_requests WHERE status = 'completed'
	`).Scan(&stats.AverageProcessingDays)

	// Count by type
	rows, _ := s.pool.Query(ctx, `
		SELECT request_type, COUNT(*) FROM data_subject_requests GROUP BY request_type
	`)
	for rows.Next() {
		var t string
		var count int
		rows.Scan(&t, &count)
		stats.ByType[t] = count
	}
	rows.Close()

	// Count by status
	rows, _ = s.pool.Query(ctx, `
		SELECT status, COUNT(*) FROM data_subject_requests GROUP BY status
	`)
	for rows.Next() {
		var s string
		var count int
		rows.Scan(&s, &count)
		stats.ByStatus[s] = count
	}
	rows.Close()

	// Upcoming deadlines (next 7 days)
	rows, _ = s.pool.Query(ctx, `
		SELECT id, request_number, request_type, status, requester_email, deadline_at
		FROM data_subject_requests
		WHERE COALESCE(extended_deadline_at, deadline_at) BETWEEN NOW() AND NOW() + INTERVAL '7 days'
		AND status NOT IN ('completed', 'rejected', 'cancelled')
		ORDER BY deadline_at ASC LIMIT 10
	`)
	for rows.Next() {
		var dsr models.DataSubjectRequest
		rows.Scan(&dsr.ID, &dsr.RequestNumber, &dsr.RequestType, &dsr.Status, &dsr.RequesterEmail, &dsr.DeadlineAt)
		stats.UpcomingDeadlines = append(stats.UpcomingDeadlines, dsr)
	}
	rows.Close()

	return stats, nil
}

// GetStatusHistory retrieves the status history for a DSR
func (s *DSRService) GetStatusHistory(ctx context.Context, requestID uuid.UUID) ([]models.DSRStatusHistory, error) {
	rows, err := s.pool.Query(ctx, `
		SELECT id, request_id, from_status, to_status, changed_by, comment, metadata, created_at
		FROM dsr_status_history WHERE request_id = $1 ORDER BY created_at DESC
	`, requestID)
	if err != nil {
		return nil, fmt.Errorf("failed to query status history: %w", err)
	}
	defer rows.Close()

	var history []models.DSRStatusHistory
	for rows.Next() {
		var h models.DSRStatusHistory
		var metadataJSON []byte
		err := rows.Scan(&h.ID, &h.RequestID, &h.FromStatus, &h.ToStatus, &h.ChangedBy, &h.Comment, &metadataJSON, &h.CreatedAt)
		if err != nil {
			continue
		}
		json.Unmarshal(metadataJSON, &h.Metadata)
		history = append(history, h)
	}

	return history, nil
}

// InitErasureExceptionChecks initializes exception checks for an erasure request
func (s *DSRService) InitErasureExceptionChecks(ctx context.Context, requestID uuid.UUID) error {
	exceptions := []struct {
		Type        string
		Description string
	}{
		{models.DSRExceptionFreedomExpression, "Ausübung des Rechts auf freie Meinungsäußerung und Information (Art. 17 Abs. 3 lit. a)"},
		{models.DSRExceptionLegalObligation, "Erfüllung einer rechtlichen Verpflichtung oder öffentlichen Aufgabe (Art. 17 Abs. 3 lit. b)"},
		{models.DSRExceptionPublicHealth, "Gründe des öffentlichen Interesses im Bereich der öffentlichen Gesundheit (Art. 17 Abs. 3 lit. c)"},
		{models.DSRExceptionArchiving, "Im öffentlichen Interesse liegende Archivzwecke, Forschung oder Statistik (Art. 17 Abs. 3 lit. d)"},
		{models.DSRExceptionLegalClaims, "Geltendmachung, Ausübung oder Verteidigung von Rechtsansprüchen (Art. 17 Abs. 3 lit. e)"},
	}

	for _, exc := range exceptions {
		_, err := s.pool.Exec(ctx, `
			INSERT INTO dsr_exception_checks (request_id, exception_type, description)
			VALUES ($1, $2, $3) ON CONFLICT DO NOTHING
		`, requestID, exc.Type, exc.Description)
		if err != nil {
			return fmt.Errorf("failed to create exception check: %w", err)
		}
	}

	return nil
}

// GetExceptionChecks retrieves exception checks for a DSR
func (s *DSRService) GetExceptionChecks(ctx context.Context, requestID uuid.UUID) ([]models.DSRExceptionCheck, error) {
	rows, err := s.pool.Query(ctx, `
		SELECT id, request_id, exception_type, description, applies, checked_by, checked_at, notes, created_at
		FROM dsr_exception_checks WHERE request_id = $1 ORDER BY created_at
	`, requestID)
	if err != nil {
		return nil, fmt.Errorf("failed to query exception checks: %w", err)
	}
	defer rows.Close()

	var checks []models.DSRExceptionCheck
	for rows.Next() {
		var c models.DSRExceptionCheck
		err := rows.Scan(&c.ID, &c.RequestID, &c.ExceptionType, &c.Description, &c.Applies,
			&c.CheckedBy, &c.CheckedAt, &c.Notes, &c.CreatedAt)
		if err != nil {
			continue
		}
		checks = append(checks, c)
	}

	return checks, nil
}

// UpdateExceptionCheck updates an exception check
func (s *DSRService) UpdateExceptionCheck(ctx context.Context, checkID uuid.UUID, applies bool, notes *string, checkedBy uuid.UUID) error {
	_, err := s.pool.Exec(ctx, `
		UPDATE dsr_exception_checks
		SET applies = $1, notes = $2, checked_by = $3, checked_at = NOW()
		WHERE id = $4
	`, applies, notes, checkedBy, checkID)
	return err
}

// ProcessDeadlines checks for approaching and overdue deadlines
func (s *DSRService) ProcessDeadlines(ctx context.Context) error {
	now := time.Now()

	// Find requests with deadlines in 3 days
	threeDaysAhead := now.AddDate(0, 0, 3)
	rows, _ := s.pool.Query(ctx, `
		SELECT id, request_number, request_type, assigned_to, deadline_at
		FROM data_subject_requests
		WHERE COALESCE(extended_deadline_at, deadline_at) BETWEEN $1 AND $2
		AND status NOT IN ('completed', 'rejected', 'cancelled')
	`, now, threeDaysAhead)
	for rows.Next() {
		var id uuid.UUID
		var requestNumber, requestType string
		var assignedTo *uuid.UUID
		var deadline time.Time
		rows.Scan(&id, &requestNumber, &requestType, &assignedTo, &deadline)

		// Notify assigned user or all DPOs
		if assignedTo != nil {
			s.notifyDeadlineWarning(ctx, id, *assignedTo, requestNumber, deadline, 3)
		} else {
			s.notifyAllDPOs(ctx, id, requestNumber, "Frist in 3 Tagen", deadline)
		}
	}
	rows.Close()

	// Find requests with deadlines in 1 day
	oneDayAhead := now.AddDate(0, 0, 1)
	rows, _ = s.pool.Query(ctx, `
		SELECT id, request_number, request_type, assigned_to, deadline_at
		FROM data_subject_requests
		WHERE COALESCE(extended_deadline_at, deadline_at) BETWEEN $1 AND $2
		AND status NOT IN ('completed', 'rejected', 'cancelled')
	`, now, oneDayAhead)
	for rows.Next() {
		var id uuid.UUID
		var requestNumber, requestType string
		var assignedTo *uuid.UUID
		var deadline time.Time
		rows.Scan(&id, &requestNumber, &requestType, &assignedTo, &deadline)

		if assignedTo != nil {
			s.notifyDeadlineWarning(ctx, id, *assignedTo, requestNumber, deadline, 1)
		} else {
			s.notifyAllDPOs(ctx, id, requestNumber, "Frist morgen!", deadline)
		}
	}
	rows.Close()

	// Find overdue requests
	rows, _ = s.pool.Query(ctx, `
		SELECT id, request_number, request_type, assigned_to, deadline_at
		FROM data_subject_requests
		WHERE COALESCE(extended_deadline_at, deadline_at) < $1
		AND status NOT IN ('completed', 'rejected', 'cancelled')
	`, now)
	for rows.Next() {
		var id uuid.UUID
		var requestNumber, requestType string
		var assignedTo *uuid.UUID
		var deadline time.Time
		rows.Scan(&id, &requestNumber, &requestType, &assignedTo, &deadline)

		// Notify all DPOs for overdue
		s.notifyAllDPOs(ctx, id, requestNumber, "ÜBERFÄLLIG!", deadline)

		// Log to audit
		s.pool.Exec(ctx, `
			INSERT INTO consent_audit_log (action, entity_type, entity_id, details)
			VALUES ('dsr_overdue', 'dsr', $1, $2)
		`, id, fmt.Sprintf(`{"request_number": "%s", "deadline": "%s"}`, requestNumber, deadline.Format(time.RFC3339)))
	}
	rows.Close()

	return nil
}
