package services

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

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5/pgxpool"
)

// NotificationType defines the type of notification
type NotificationType string

const (
	NotificationTypeConsentRequired  NotificationType = "consent_required"
	NotificationTypeConsentReminder  NotificationType = "consent_reminder"
	NotificationTypeVersionPublished NotificationType = "version_published"
	NotificationTypeVersionApproved  NotificationType = "version_approved"
	NotificationTypeVersionRejected  NotificationType = "version_rejected"
	NotificationTypeAccountSuspended NotificationType = "account_suspended"
	NotificationTypeAccountRestored  NotificationType = "account_restored"
	NotificationTypeGeneral          NotificationType = "general"
	// DSR (Data Subject Request) notification types
	NotificationTypeDSRReceived NotificationType = "dsr_received"
	NotificationTypeDSRAssigned NotificationType = "dsr_assigned"
	NotificationTypeDSRDeadline NotificationType = "dsr_deadline"
)

// NotificationChannel defines how notification is delivered
type NotificationChannel string

const (
	ChannelInApp NotificationChannel = "in_app"
	ChannelEmail NotificationChannel = "email"
	ChannelPush  NotificationChannel = "push"
)

// Notification represents a notification entity
type Notification struct {
	ID        uuid.UUID              `json:"id"`
	UserID    uuid.UUID              `json:"user_id"`
	Type      NotificationType       `json:"type"`
	Channel   NotificationChannel    `json:"channel"`
	Title     string                 `json:"title"`
	Body      string                 `json:"body"`
	Data      map[string]interface{} `json:"data,omitempty"`
	ReadAt    *time.Time             `json:"read_at,omitempty"`
	SentAt    *time.Time             `json:"sent_at,omitempty"`
	CreatedAt time.Time              `json:"created_at"`
}

// NotificationPreferences holds user notification settings
type NotificationPreferences struct {
	UserID            uuid.UUID `json:"user_id"`
	EmailEnabled      bool      `json:"email_enabled"`
	PushEnabled       bool      `json:"push_enabled"`
	InAppEnabled      bool      `json:"in_app_enabled"`
	ReminderFrequency string    `json:"reminder_frequency"`
}

// NotificationService handles notification operations
type NotificationService struct {
	pool         *pgxpool.Pool
	emailService *EmailService
}

// NewNotificationService creates a new notification service
func NewNotificationService(pool *pgxpool.Pool, emailService *EmailService) *NotificationService {
	return &NotificationService{
		pool:         pool,
		emailService: emailService,
	}
}

// CreateNotification creates and optionally sends a notification
func (s *NotificationService) CreateNotification(ctx context.Context, userID uuid.UUID, notifType NotificationType, title, body string, data map[string]interface{}) error {
	// Get user preferences
	prefs, err := s.GetPreferences(ctx, userID)
	if err != nil {
		// Use default preferences if not found
		prefs = &NotificationPreferences{
			UserID:       userID,
			EmailEnabled: true,
			PushEnabled:  true,
			InAppEnabled: true,
		}
	}

	// Create in-app notification if enabled
	if prefs.InAppEnabled {
		if err := s.createInAppNotification(ctx, userID, notifType, title, body, data); err != nil {
			return fmt.Errorf("failed to create in-app notification: %w", err)
		}
	}

	// Send email notification if enabled
	if prefs.EmailEnabled && s.emailService != nil {
		go s.sendEmailNotification(ctx, userID, notifType, title, body, data)
	}

	// Push notification would be sent here if enabled
	// if prefs.PushEnabled {
	//     go s.sendPushNotification(ctx, userID, title, body, data)
	// }

	return nil
}

// createInAppNotification creates an in-app notification
func (s *NotificationService) createInAppNotification(ctx context.Context, userID uuid.UUID, notifType NotificationType, title, body string, data map[string]interface{}) error {
	dataJSON, _ := json.Marshal(data)

	_, err := s.pool.Exec(ctx, `
		INSERT INTO notifications (user_id, type, channel, title, body, data, created_at)
		VALUES ($1, $2, $3, $4, $5, $6, NOW())
	`, userID, notifType, ChannelInApp, title, body, dataJSON)

	return err
}

// sendEmailNotification sends an email notification
func (s *NotificationService) sendEmailNotification(ctx context.Context, userID uuid.UUID, notifType NotificationType, title, body string, data map[string]interface{}) {
	// Get user email
	var email string
	err := s.pool.QueryRow(ctx, `SELECT email FROM users WHERE id = $1`, userID).Scan(&email)
	if err != nil {
		return
	}

	// Send based on notification type
	switch notifType {
	case NotificationTypeConsentRequired, NotificationTypeConsentReminder:
		s.emailService.SendConsentReminderEmail(email, title, body)
	default:
		s.emailService.SendGenericNotificationEmail(email, title, body)
	}

	// Mark as sent
	s.pool.Exec(ctx, `
		UPDATE notifications SET sent_at = NOW()
		WHERE user_id = $1 AND type = $2 AND channel = $3 AND sent_at IS NULL
		ORDER BY created_at DESC LIMIT 1
	`, userID, notifType, ChannelEmail)
}

// GetUserNotifications returns notifications for a user
func (s *NotificationService) GetUserNotifications(ctx context.Context, userID uuid.UUID, limit, offset int, unreadOnly bool) ([]Notification, int, error) {
	// Count total
	var totalQuery string
	var total int

	if unreadOnly {
		totalQuery = `SELECT COUNT(*) FROM notifications WHERE user_id = $1 AND read_at IS NULL`
	} else {
		totalQuery = `SELECT COUNT(*) FROM notifications WHERE user_id = $1`
	}
	s.pool.QueryRow(ctx, totalQuery, userID).Scan(&total)

	// Get notifications
	var query string
	if unreadOnly {
		query = `
			SELECT id, user_id, type, channel, title, body, data, read_at, sent_at, created_at
			FROM notifications
			WHERE user_id = $1 AND read_at IS NULL
			ORDER BY created_at DESC
			LIMIT $2 OFFSET $3
		`
	} else {
		query = `
			SELECT id, user_id, type, channel, title, body, data, read_at, sent_at, created_at
			FROM notifications
			WHERE user_id = $1
			ORDER BY created_at DESC
			LIMIT $2 OFFSET $3
		`
	}

	rows, err := s.pool.Query(ctx, query, userID, limit, offset)
	if err != nil {
		return nil, 0, err
	}
	defer rows.Close()

	var notifications []Notification
	for rows.Next() {
		var n Notification
		var dataJSON []byte
		if err := rows.Scan(&n.ID, &n.UserID, &n.Type, &n.Channel, &n.Title, &n.Body, &dataJSON, &n.ReadAt, &n.SentAt, &n.CreatedAt); err != nil {
			continue
		}
		if dataJSON != nil {
			json.Unmarshal(dataJSON, &n.Data)
		}
		notifications = append(notifications, n)
	}

	return notifications, total, nil
}

// GetUnreadCount returns the count of unread notifications
func (s *NotificationService) GetUnreadCount(ctx context.Context, userID uuid.UUID) (int, error) {
	var count int
	err := s.pool.QueryRow(ctx, `
		SELECT COUNT(*) FROM notifications WHERE user_id = $1 AND read_at IS NULL
	`, userID).Scan(&count)
	return count, err
}

// MarkAsRead marks a notification as read
func (s *NotificationService) MarkAsRead(ctx context.Context, userID uuid.UUID, notificationID uuid.UUID) error {
	result, err := s.pool.Exec(ctx, `
		UPDATE notifications SET read_at = NOW()
		WHERE id = $1 AND user_id = $2 AND read_at IS NULL
	`, notificationID, userID)

	if err != nil {
		return err
	}
	if result.RowsAffected() == 0 {
		return fmt.Errorf("notification not found or already read")
	}
	return nil
}

// MarkAllAsRead marks all notifications as read for a user
func (s *NotificationService) MarkAllAsRead(ctx context.Context, userID uuid.UUID) error {
	_, err := s.pool.Exec(ctx, `
		UPDATE notifications SET read_at = NOW()
		WHERE user_id = $1 AND read_at IS NULL
	`, userID)
	return err
}

// DeleteNotification deletes a notification
func (s *NotificationService) DeleteNotification(ctx context.Context, userID uuid.UUID, notificationID uuid.UUID) error {
	result, err := s.pool.Exec(ctx, `
		DELETE FROM notifications WHERE id = $1 AND user_id = $2
	`, notificationID, userID)

	if err != nil {
		return err
	}
	if result.RowsAffected() == 0 {
		return fmt.Errorf("notification not found")
	}
	return nil
}

// GetPreferences returns notification preferences for a user
func (s *NotificationService) GetPreferences(ctx context.Context, userID uuid.UUID) (*NotificationPreferences, error) {
	var prefs NotificationPreferences
	prefs.UserID = userID

	err := s.pool.QueryRow(ctx, `
		SELECT email_enabled, push_enabled, in_app_enabled, reminder_frequency
		FROM notification_preferences
		WHERE user_id = $1
	`, userID).Scan(&prefs.EmailEnabled, &prefs.PushEnabled, &prefs.InAppEnabled, &prefs.ReminderFrequency)

	if err != nil {
		// Return defaults if not found
		return &NotificationPreferences{
			UserID:            userID,
			EmailEnabled:      true,
			PushEnabled:       true,
			InAppEnabled:      true,
			ReminderFrequency: "weekly",
		}, nil
	}

	return &prefs, nil
}

// UpdatePreferences updates notification preferences for a user
func (s *NotificationService) UpdatePreferences(ctx context.Context, userID uuid.UUID, prefs *NotificationPreferences) error {
	_, err := s.pool.Exec(ctx, `
		INSERT INTO notification_preferences (user_id, email_enabled, push_enabled, in_app_enabled, reminder_frequency, updated_at)
		VALUES ($1, $2, $3, $4, $5, NOW())
		ON CONFLICT (user_id) DO UPDATE SET
			email_enabled = $2,
			push_enabled = $3,
			in_app_enabled = $4,
			reminder_frequency = $5,
			updated_at = NOW()
	`, userID, prefs.EmailEnabled, prefs.PushEnabled, prefs.InAppEnabled, prefs.ReminderFrequency)

	return err
}

// NotifyConsentRequired sends consent required notifications to all active users
func (s *NotificationService) NotifyConsentRequired(ctx context.Context, documentName, versionID string) error {
	// Get all active users
	rows, err := s.pool.Query(ctx, `
		SELECT id FROM users WHERE account_status = 'active'
	`)
	if err != nil {
		return err
	}
	defer rows.Close()

	title := "Neue Zustimmung erforderlich"
	body := fmt.Sprintf("Eine neue Version von '%s' wurde veröffentlicht. Bitte überprüfen und bestätigen Sie diese.", documentName)
	data := map[string]interface{}{
		"version_id":    versionID,
		"document_name": documentName,
	}

	for rows.Next() {
		var userID uuid.UUID
		if err := rows.Scan(&userID); err != nil {
			continue
		}
		go s.CreateNotification(ctx, userID, NotificationTypeConsentRequired, title, body, data)
	}

	return nil
}

// NotifyVersionApproved notifies the creator that their version was approved
func (s *NotificationService) NotifyVersionApproved(ctx context.Context, creatorID uuid.UUID, documentName, versionNumber, approverEmail string) error {
	title := "Version genehmigt"
	body := fmt.Sprintf("Ihre Version %s von '%s' wurde von %s genehmigt und kann nun veröffentlicht werden.", versionNumber, documentName, approverEmail)
	data := map[string]interface{}{
		"document_name":  documentName,
		"version_number": versionNumber,
		"approver":       approverEmail,
	}

	return s.CreateNotification(ctx, creatorID, NotificationTypeVersionApproved, title, body, data)
}

// NotifyVersionRejected notifies the creator that their version was rejected
func (s *NotificationService) NotifyVersionRejected(ctx context.Context, creatorID uuid.UUID, documentName, versionNumber, reason, rejecterEmail string) error {
	title := "Version abgelehnt"
	body := fmt.Sprintf("Ihre Version %s von '%s' wurde von %s abgelehnt. Grund: %s", versionNumber, documentName, rejecterEmail, reason)
	data := map[string]interface{}{
		"document_name":  documentName,
		"version_number": versionNumber,
		"rejecter":       rejecterEmail,
		"reason":         reason,
	}

	return s.CreateNotification(ctx, creatorID, NotificationTypeVersionRejected, title, body, data)
}
