package notify

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"sync"
	"time"

	"gitea.dooplex.hu/admin/felhom-hub/internal/store"
)

// Dispatcher routes events to operator and/or customer email channels.
// Cooldowns are in-memory (lost on restart, acceptable).
type Dispatcher struct {
	store         *store.Store
	resendAPIKey  string
	fromEmail     string
	operatorEmail string
	operatorOn    bool
	httpClient    *http.Client
	logger        *log.Logger

	mu            sync.Mutex
	opCooldowns   map[string]time.Time // "customerID:eventType" → last operator notify
	custCooldowns map[string]time.Time // "customerID:eventType" → last customer notify
}

// NewDispatcher creates a new notification dispatcher.
func NewDispatcher(s *store.Store, resendAPIKey, fromEmail, operatorEmail string, operatorOn bool, logger *log.Logger) *Dispatcher {
	return &Dispatcher{
		store:         s,
		resendAPIKey:  resendAPIKey,
		fromEmail:     fromEmail,
		operatorEmail: operatorEmail,
		operatorOn:    operatorOn,
		httpClient:    &http.Client{Timeout: 10 * time.Second},
		logger:        logger,
		opCooldowns:   make(map[string]time.Time),
		custCooldowns: make(map[string]time.Time),
	}
}

// ProcessEvent evaluates an event and sends notifications as appropriate.
// Safe to call from goroutines.
func (d *Dispatcher) ProcessEvent(customerID, eventType, severity, message, detailsJSON, source string) {
	if d.resendAPIKey == "" {
		return
	}

	// "test" bypass — send directly to customer email, skip prefs/cooldown
	if eventType == "test" {
		d.sendTestEmail(customerID)
		return
	}

	// Only warning and error severity trigger notifications
	if severity != "warning" && severity != "error" {
		return
	}

	// Operator channel
	d.processOperator(customerID, eventType, severity, message, detailsJSON, source)

	// Customer channel
	d.processCustomer(customerID, eventType, severity, message, detailsJSON, source)
}

func (d *Dispatcher) sendTestEmail(customerID string) {
	prefs, err := d.store.GetNotificationPrefs(customerID)
	if err != nil || prefs.Email == "" {
		d.logger.Printf("[WARN] Test email: no email configured for %s", customerID)
		return
	}

	subject := "[Felhom] Teszt értesítés"
	body := "Kedves Ügyfél!\n\nEz egy teszt értesítés a Felhom monitoring rendszerből.\nAz értesítések megfelelően működnek.\n\nÜdvözlettel,\nFelhom.eu monitoring"

	if err := d.sendEmail(prefs.Email, subject, body); err != nil {
		d.logger.Printf("[ERROR] Test email to %s failed: %v", prefs.Email, err)
		d.store.LogNotification(customerID, "test", "info", "Teszt értesítés", "failed", err.Error(), "customer")
		return
	}
	d.logger.Printf("[INFO] Test email sent to %s for %s", prefs.Email, customerID)
	d.store.LogNotification(customerID, "test", "info", "Teszt értesítés", "sent", "", "customer")
}

func (d *Dispatcher) processOperator(customerID, eventType, severity, message, detailsJSON, source string) {
	if !d.operatorOn || d.operatorEmail == "" {
		return
	}

	cooldownKey := customerID + ":" + eventType
	d.mu.Lock()
	if last, ok := d.opCooldowns[cooldownKey]; ok && time.Since(last) < 1*time.Hour {
		d.mu.Unlock()
		return
	}
	d.opCooldowns[cooldownKey] = time.Now()
	d.mu.Unlock()

	subject, body := FormatOperatorEmail(customerID, eventType, severity, message, detailsJSON)

	if err := d.sendEmail(d.operatorEmail, subject, body); err != nil {
		d.logger.Printf("[ERROR] Operator email failed for %s/%s: %v", customerID, eventType, err)
		d.store.LogNotification(customerID, eventType, severity, message, "failed", err.Error(), "operator")
		return
	}
	d.logger.Printf("[INFO] Operator email sent for %s/%s", customerID, eventType)
	d.store.LogNotification(customerID, eventType, severity, message, "sent", "", "operator")
}

func (d *Dispatcher) processCustomer(customerID, eventType, severity, message, detailsJSON, source string) {
	// Check if customer is blocked
	if d.store.IsCustomerBlocked(customerID) {
		return
	}

	// Load preferences
	prefs, err := d.store.GetNotificationPrefs(customerID)
	if err != nil || prefs.Email == "" {
		return
	}

	// Check if event type is enabled
	if !isEventEnabled(prefs.EnabledEvents, eventType) {
		return
	}

	// Customer cooldown (from prefs, default 6h)
	cooldownHours := prefs.CooldownHours
	if cooldownHours <= 0 {
		cooldownHours = 6
	}
	cooldownDur := time.Duration(cooldownHours) * time.Hour

	cooldownKey := customerID + ":" + eventType
	d.mu.Lock()
	if last, ok := d.custCooldowns[cooldownKey]; ok && time.Since(last) < cooldownDur {
		d.mu.Unlock()
		return
	}
	d.custCooldowns[cooldownKey] = time.Now()
	d.mu.Unlock()

	subject, body := FormatCustomerEmail(customerID, eventType, severity, message, detailsJSON)

	if err := d.sendEmail(prefs.Email, subject, body); err != nil {
		d.logger.Printf("[ERROR] Customer email failed for %s/%s: %v", customerID, eventType, err)
		d.store.LogNotification(customerID, eventType, severity, message, "failed", err.Error(), "customer")
		return
	}
	d.logger.Printf("[INFO] Customer email sent to %s for %s/%s", prefs.Email, customerID, eventType)
	d.store.LogNotification(customerID, eventType, severity, message, "sent", "", "customer")
}

func (d *Dispatcher) sendEmail(to, subject, textBody string) error {
	payload := map[string]interface{}{
		"from":    d.fromEmail,
		"to":      []string{to},
		"subject": subject,
		"text":    textBody,
	}

	jsonData, err := json.Marshal(payload)
	if err != nil {
		return fmt.Errorf("marshaling email payload: %w", err)
	}

	req, err := http.NewRequest("POST", "https://api.resend.com/emails", bytes.NewReader(jsonData))
	if err != nil {
		return fmt.Errorf("creating request: %w", err)
	}
	req.Header.Set("Authorization", "Bearer "+d.resendAPIKey)
	req.Header.Set("Content-Type", "application/json")

	resp, err := d.httpClient.Do(req)
	if err != nil {
		return fmt.Errorf("sending request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		respBody, _ := io.ReadAll(io.LimitReader(resp.Body, 1024))
		return fmt.Errorf("resend API returned %d: %s", resp.StatusCode, string(respBody))
	}

	return nil
}

func isEventEnabled(enabledEvents []string, eventType string) bool {
	for _, e := range enabledEvents {
		if e == eventType {
			return true
		}
	}
	return false
}
