package handlers

import (
	"net/http"
	"strings"

	"github.com/flatrender/notification-svc/internal/db"
	"github.com/flatrender/notification-svc/internal/middleware"
	"github.com/flatrender/notification-svc/internal/models"
	"github.com/flatrender/notification-svc/internal/sender"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

type ChannelHandler struct{ store *db.Store }

func NewChannelHandler(s *db.Store) *ChannelHandler { return &ChannelHandler{store: s} }

func (h *ChannelHandler) tenant(c *gin.Context) uuid.UUID {
	v, _ := c.Get(middleware.CtxTenantID)
	id, _ := v.(uuid.UUID)
	return id
}
func (h *ChannelHandler) user(c *gin.Context) uuid.UUID {
	v, _ := c.Get(middleware.CtxUserID)
	id, _ := v.(uuid.UUID)
	return id
}

// GET /v1/channels — both provider configs, secrets masked.
func (h *ChannelHandler) ListConfigs(c *gin.Context) {
	cfgs, err := h.store.GetChannelConfigs(c.Request.Context(), h.tenant(c))
	if err != nil {
		c.JSON(http.StatusInternalServerError, models.APIError{Code: "db_error", Message: err.Error()})
		return
	}
	out := map[string]*models.ChannelConfig{
		"sms":   {Channel: "sms", Settings: map[string]any{}},
		"email": {Channel: "email", Settings: map[string]any{}},
	}
	for _, cf := range cfgs {
		out[cf.Channel] = cf
	}
	mask(out["sms"], "api_key")
	mask(out["email"], "password")
	c.JSON(http.StatusOK, gin.H{"sms": out["sms"], "email": out["email"]})
}

// PUT /v1/channels/:channel — upsert provider settings (blank secret keeps existing).
func (h *ChannelHandler) UpsertConfig(c *gin.Context) {
	channel := c.Param("channel")
	if channel != "sms" && channel != "email" {
		c.JSON(http.StatusBadRequest, models.APIError{Code: "bad_request", Message: "channel must be sms or email"})
		return
	}
	var req models.UpsertChannelConfigRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, models.APIError{Code: "bad_request", Message: err.Error()})
		return
	}
	settings := req.Settings
	if settings == nil {
		settings = map[string]any{}
	}
	enabled := false
	if req.Enabled != nil {
		enabled = *req.Enabled
	}
	secretKey := "api_key"
	if channel == "email" {
		secretKey = "password"
	}
	// Preserve the secret if the client sent it blank/omitted (it's masked on read).
	existing, _ := h.store.GetChannelConfig(c.Request.Context(), h.tenant(c), channel)
	if existing != nil {
		if v, ok := settings[secretKey]; !ok || str(v) == "" || strings.Contains(str(v), "•") {
			if ev, ok := existing.Settings[secretKey]; ok {
				settings[secretKey] = ev
			}
		}
	}
	if err := h.store.UpsertChannelConfig(c.Request.Context(), h.tenant(c), channel, settings, enabled); err != nil {
		c.JSON(http.StatusInternalServerError, models.APIError{Code: "db_error", Message: err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"ok": true})
}

// POST /v1/sms/send — send an SMS via Kavenegar.
func (h *ChannelHandler) SendSMS(c *gin.Context) {
	var req models.SendSMSRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, models.APIError{Code: "bad_request", Message: err.Error()})
		return
	}
	cfg, _ := h.store.GetChannelConfig(c.Request.Context(), h.tenant(c), "sms")
	if cfg == nil || !cfg.Enabled {
		c.JSON(http.StatusBadRequest, models.APIError{Code: "not_configured", Message: "SMS channel is not configured/enabled"})
		return
	}
	msgID, err := sender.SendSMS(str(cfg.Settings["api_key"]), str(cfg.Settings["line_number"]), req.To, req.Message)
	status, errMsg := "Sent", (*string)(nil)
	if err != nil {
		status = "Failed"
		e := err.Error()
		errMsg = &e
	}
	provider := "kavenegar"
	_ = h.store.LogDelivery(c.Request.Context(), h.tenant(c), h.user(c), "SMS", req.To, nil, &provider, ptr(msgID), &status, errMsg)
	if err != nil {
		c.JSON(http.StatusBadGateway, models.APIError{Code: "send_failed", Message: err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"status": "sent", "message_id": msgID})
}

// POST /v1/email/send — send an email via SMTP (optionally from a template + variables).
func (h *ChannelHandler) SendEmail(c *gin.Context) {
	var req models.SendEmailRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, models.APIError{Code: "bad_request", Message: err.Error()})
		return
	}
	subject, html := req.Subject, req.BodyHTML
	if req.TemplateCode != "" {
		locale := req.Locale
		if locale == "" {
			locale = "fa"
		}
		tpl, _ := h.store.GetEmailTemplate(c.Request.Context(), req.TemplateCode, locale)
		if tpl == nil {
			c.JSON(http.StatusNotFound, models.APIError{Code: "not_found", Message: "email template not found"})
			return
		}
		if tpl.Subject != nil {
			subject = *tpl.Subject
		}
		if tpl.BodyHTML != nil {
			html = *tpl.BodyHTML
		}
		for k, v := range req.Variables {
			subject = strings.ReplaceAll(subject, "{{"+k+"}}", v)
			html = strings.ReplaceAll(html, "{{"+k+"}}", v)
		}
	}
	cfg, _ := h.store.GetChannelConfig(c.Request.Context(), h.tenant(c), "email")
	if cfg == nil || !cfg.Enabled {
		c.JSON(http.StatusBadRequest, models.APIError{Code: "not_configured", Message: "Email channel is not configured/enabled"})
		return
	}
	scfg := sender.SMTPConfig{
		Host:      str(cfg.Settings["host"]),
		Port:      intv(cfg.Settings["port"]),
		Username:  str(cfg.Settings["username"]),
		Password:  str(cfg.Settings["password"]),
		FromEmail: str(cfg.Settings["from_email"]),
		FromName:  str(cfg.Settings["from_name"]),
		UseTLS:    boolv(cfg.Settings["use_tls"]),
	}
	err := sender.SendEmail(scfg, req.To, subject, html)
	status, errMsg := "Sent", (*string)(nil)
	if err != nil {
		status = "Failed"
		e := err.Error()
		errMsg = &e
	}
	provider := "smtp"
	_ = h.store.LogDelivery(c.Request.Context(), h.tenant(c), h.user(c), "Email", req.To, &subject, &provider, nil, &status, errMsg)
	if err != nil {
		c.JSON(http.StatusBadGateway, models.APIError{Code: "send_failed", Message: err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"status": "sent"})
}

// ── helpers ──────────────────────────────────────────────────────────────────

func mask(cfg *models.ChannelConfig, key string) {
	if cfg == nil || cfg.Settings == nil {
		return
	}
	if v, ok := cfg.Settings[key]; ok && str(v) != "" {
		s := str(v)
		if len(s) > 4 {
			cfg.Settings[key] = "••••••••" + s[len(s)-4:]
		} else {
			cfg.Settings[key] = "••••"
		}
	}
}

func str(v any) string {
	if s, ok := v.(string); ok {
		return s
	}
	return ""
}
func intv(v any) int {
	switch n := v.(type) {
	case float64:
		return int(n)
	case int:
		return n
	}
	return 0
}
func boolv(v any) bool {
	b, _ := v.(bool)
	return b
}
func ptr(s string) *string {
	if s == "" {
		return nil
	}
	return &s
}
