package services

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

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

// GetCommunications retrieves communications for a DSR
func (s *DSRService) GetCommunications(ctx context.Context, requestID uuid.UUID) ([]models.DSRCommunication, error) {
	rows, err := s.pool.Query(ctx, `
		SELECT id, request_id, direction, channel, communication_type, template_version_id,
			subject, body_html, body_text, recipient_email, sent_at, error_message,
			attachments, created_at, created_by
		FROM dsr_communications WHERE request_id = $1 ORDER BY created_at DESC
	`, requestID)
	if err != nil {
		return nil, fmt.Errorf("failed to query communications: %w", err)
	}
	defer rows.Close()

	var comms []models.DSRCommunication
	for rows.Next() {
		var c models.DSRCommunication
		var attachmentsJSON []byte
		err := rows.Scan(&c.ID, &c.RequestID, &c.Direction, &c.Channel, &c.CommunicationType,
			&c.TemplateVersionID, &c.Subject, &c.BodyHTML, &c.BodyText, &c.RecipientEmail,
			&c.SentAt, &c.ErrorMessage, &attachmentsJSON, &c.CreatedAt, &c.CreatedBy)
		if err != nil {
			continue
		}
		json.Unmarshal(attachmentsJSON, &c.Attachments)
		comms = append(comms, c)
	}

	return comms, nil
}

// SendCommunication sends a communication for a DSR
func (s *DSRService) SendCommunication(ctx context.Context, requestID uuid.UUID, req models.SendDSRCommunicationRequest, sentBy uuid.UUID) error {
	// Get DSR details
	dsr, err := s.GetByID(ctx, requestID)
	if err != nil {
		return err
	}

	// Get template if specified
	var subject, bodyHTML, bodyText string
	if req.TemplateVersionID != nil {
		templateVersionID, _ := uuid.Parse(*req.TemplateVersionID)
		err := s.pool.QueryRow(ctx, `
			SELECT subject, body_html, body_text FROM dsr_template_versions WHERE id = $1 AND status = 'published'
		`, templateVersionID).Scan(&subject, &bodyHTML, &bodyText)
		if err != nil {
			return fmt.Errorf("template version not found or not published: %w", err)
		}
	}

	// Use custom content if provided
	if req.CustomSubject != nil {
		subject = *req.CustomSubject
	}
	if req.CustomBody != nil {
		bodyHTML = *req.CustomBody
		bodyText = stripHTML(*req.CustomBody)
	}

	// Replace variables
	variables := map[string]string{
		"requester_name":  stringOrDefault(dsr.RequesterName, "Antragsteller/in"),
		"request_number":  dsr.RequestNumber,
		"request_type_de": dsr.RequestType.Label(),
		"request_date":    dsr.CreatedAt.Format("02.01.2006"),
		"deadline_date":   dsr.DeadlineAt.Format("02.01.2006"),
	}
	for k, v := range req.Variables {
		variables[k] = v
	}
	subject = replaceVariables(subject, variables)
	bodyHTML = replaceVariables(bodyHTML, variables)
	bodyText = replaceVariables(bodyText, variables)

	// Send email
	if s.emailService != nil {
		err = s.emailService.SendEmail(dsr.RequesterEmail, subject, bodyHTML, bodyText)
		if err != nil {
			// Log error but continue
			_, _ = s.pool.Exec(ctx, `
				INSERT INTO dsr_communications (request_id, direction, channel, communication_type,
					template_version_id, subject, body_html, body_text, recipient_email, error_message, created_by)
				VALUES ($1, 'outbound', 'email', $2, $3, $4, $5, $6, $7, $8, $9)
			`, requestID, req.CommunicationType, req.TemplateVersionID, subject, bodyHTML, bodyText,
				dsr.RequesterEmail, err.Error(), sentBy)
			return fmt.Errorf("failed to send email: %w", err)
		}
	}

	// Log communication
	now := time.Now()
	_, err = s.pool.Exec(ctx, `
		INSERT INTO dsr_communications (request_id, direction, channel, communication_type,
			template_version_id, subject, body_html, body_text, recipient_email, sent_at, created_by)
		VALUES ($1, 'outbound', 'email', $2, $3, $4, $5, $6, $7, $8, $9)
	`, requestID, req.CommunicationType, req.TemplateVersionID, subject, bodyHTML, bodyText,
		dsr.RequesterEmail, now, sentBy)

	return err
}

// --- Notification helpers ---

func (s *DSRService) notifyNewRequest(ctx context.Context, dsr *models.DataSubjectRequest) {
	if s.notificationService == nil {
		return
	}
	// Notify all DPOs
	rows, _ := s.pool.Query(ctx, "SELECT id FROM users WHERE role = 'data_protection_officer'")
	defer rows.Close()
	for rows.Next() {
		var userID uuid.UUID
		rows.Scan(&userID)
		s.notificationService.CreateNotification(ctx, userID, NotificationTypeDSRReceived,
			"Neue Betroffenenanfrage",
			fmt.Sprintf("Neue %s eingegangen: %s", dsr.RequestType.Label(), dsr.RequestNumber),
			map[string]interface{}{"dsr_id": dsr.ID, "request_number": dsr.RequestNumber})
	}
}

func (s *DSRService) notifyAssignment(ctx context.Context, dsrID, assigneeID uuid.UUID) {
	if s.notificationService == nil {
		return
	}
	dsr, _ := s.GetByID(ctx, dsrID)
	if dsr != nil {
		s.notificationService.CreateNotification(ctx, assigneeID, NotificationTypeDSRAssigned,
			"Betroffenenanfrage zugewiesen",
			fmt.Sprintf("Ihnen wurde die Anfrage %s zugewiesen", dsr.RequestNumber),
			map[string]interface{}{"dsr_id": dsrID, "request_number": dsr.RequestNumber})
	}
}

func (s *DSRService) notifyDeadlineWarning(ctx context.Context, dsrID, userID uuid.UUID, requestNumber string, deadline time.Time, daysLeft int) {
	if s.notificationService == nil {
		return
	}
	s.notificationService.CreateNotification(ctx, userID, NotificationTypeDSRDeadline,
		fmt.Sprintf("Fristwarnung: %s", requestNumber),
		fmt.Sprintf("Die Frist für %s läuft in %d Tag(en) ab (%s)", requestNumber, daysLeft, deadline.Format("02.01.2006")),
		map[string]interface{}{"dsr_id": dsrID, "deadline": deadline, "days_left": daysLeft})
}

func (s *DSRService) notifyAllDPOs(ctx context.Context, dsrID uuid.UUID, requestNumber, message string, deadline time.Time) {
	if s.notificationService == nil {
		return
	}
	rows, _ := s.pool.Query(ctx, "SELECT id FROM users WHERE role = 'data_protection_officer'")
	defer rows.Close()
	for rows.Next() {
		var userID uuid.UUID
		rows.Scan(&userID)
		s.notificationService.CreateNotification(ctx, userID, NotificationTypeDSRDeadline,
			fmt.Sprintf("%s: %s", message, requestNumber),
			fmt.Sprintf("Anfrage %s: %s (Frist: %s)", requestNumber, message, deadline.Format("02.01.2006")),
			map[string]interface{}{"dsr_id": dsrID, "deadline": deadline})
	}
}

// --- String utility helpers ---

func stringOrDefault(s *string, def string) string {
	if s != nil {
		return *s
	}
	return def
}

func replaceVariables(text string, variables map[string]string) string {
	for k, v := range variables {
		text = strings.ReplaceAll(text, "{{"+k+"}}", v)
	}
	return text
}

func stripHTML(html string) string {
	// Simple HTML stripping - in production use a proper library
	text := strings.ReplaceAll(html, "<br>", "\n")
	text = strings.ReplaceAll(text, "<br/>", "\n")
	text = strings.ReplaceAll(text, "<br />", "\n")
	text = strings.ReplaceAll(text, "</p>", "\n\n")
	// Remove all remaining tags
	for {
		start := strings.Index(text, "<")
		if start == -1 {
			break
		}
		end := strings.Index(text[start:], ">")
		if end == -1 {
			break
		}
		text = text[:start] + text[start+end+1:]
	}
	return strings.TrimSpace(text)
}
