package email

import (
	"bytes"
	"crypto/rand"
	"crypto/tls"
	"encoding/hex"
	"fmt"
	"html/template"
	"net/url"
	"os"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"time"

	"fotbal-club/internal/config"
	"fotbal-club/internal/models"
	"fotbal-club/pkg/logger"
	"fotbal-club/pkg/utils"

	"github.com/vanng822/go-premailer/premailer"
	"gopkg.in/mail.v2"
	"gorm.io/gorm"
)

type EmailData struct {
	Subject  string
	To       []string
	CC       []string
	BCC      []string
	Template string
	Data     interface{}
	From     string
	FromName string
}

type EmailService interface {
	SendEmail(data *EmailData) error
	SendContactForm(data *ContactFormData) error
	SendNewsletter(data *NewsletterData) error
	SendNewsletterWelcome(data *NewsletterWelcomeData) error
	SendNewsletterWelcomeBack(data *NewsletterWelcomeBackData) error
	SendPasswordResetCode(to string, code string) error
	SendPasswordReset(to string, resetLink string, useOverride bool) error
	SendAdminWelcome(to string) error
}

type emailService struct {
	config *config.Config
	db     *gorm.DB
}

// SendAdminWelcome sends a welcome email to the new admin using the fixed
// system SMTP account (system@tdvorak.dev), so it works even before club SMTP
// is configured. The content uses templates/emails/admin_welcome.html rendered
// inside base.html and includes basic branding pulled from DB Settings.
func (s *emailService) SendAdminWelcome(to string) error {
	// Fixed system-only SMTP (same as password recovery)
	const host = "smtp.purelymail.com"
	const port = 465
	const user = "system@tdvorak.dev"
	const pass = "RzK1BqZAUcC%"
	const from = "system@tdvorak.dev"
	const fromName = "System MyClub"

	d := mail.NewDialer(host, port, user, pass)
	d.SSL = true
	d.TLSConfig = &tls.Config{InsecureSkipVerify: false, ServerName: host}
	d.Timeout = 30 * time.Second

	// Branding/settings (best-effort)
	var set models.Settings
	if s.db != nil {
		_ = s.db.First(&set).Error
	}
	clubName := strings.TrimSpace(set.ClubName)
	if clubName == "" {
		clubName = "Fotbal Club"
	}
	clubLogo := strings.TrimSpace(set.ClubLogoURL)
	if clubLogo == "" {
		if clubID := strings.TrimSpace(set.ClubID); clubID != "" {
			// Use PNG format for better email client compatibility (SVG not widely supported)
			clubLogo = fmt.Sprintf("https://logoapi.sportcreative.eu/logos/%s?format=png&width=400", clubID)
		}
	}
	if clubLogo == "" {
		clubLogo = "https://via.placeholder.com/400x400.png?text=Logo"
	}
	primaryColor := strings.TrimSpace(set.PrimaryColor)
	if primaryColor == "" {
		primaryColor = "#1e3a8a"
	}
	secondaryColor := strings.TrimSpace(set.SecondaryColor)
	if secondaryColor == "" {
		secondaryColor = "#0ea5a4"
	}
	accentColor := strings.TrimSpace(set.AccentColor)
	if accentColor == "" {
		accentColor = "#2563eb"
	}

	baseFE := strings.TrimSuffix(s.config.FrontendBaseURL, "/")
	websiteURL := strings.TrimSpace(set.CanonicalBaseURL)

	// Template variables
	vm := map[string]interface{}{
		"Subject":        "Vítejte v MyClub – správa webu klubu",
		"ClubName":       clubName,
		"ClubLogoURL":    clubLogo,
		"PrimaryColor":   primaryColor,
		"SecondaryColor": secondaryColor,
		"AccentColor":    accentColor,
		"FacebookURL":    getStringField(set, "FacebookURL"),
		"InstagramURL":   getStringField(set, "InstagramURL"),
		"YouTubeURL": func() string {
			v := getStringField(set, "YouTubeURL")
			if v == "" {
				v = getStringField(set, "YoutubeURL")
			}
			return v
		}(),
		"TwitterURL": getStringField(set, "TwitterURL"),
		// Links used by base template buttons/sections
		"WebsiteURL": websiteURL,
		// Convenience links used in the admin welcome content
		"LoginURL": func() string {
			if baseFE == "" {
				return ""
			}
			return baseFE + "/login"
		}(),
		"DashboardURL": func() string {
			if baseFE == "" {
				return ""
			}
			return baseFE + "/admin"
		}(),
		"DocsURL": func() string {
			if baseFE == "" {
				return ""
			}
			return baseFE + "/admin/docs"
		}(),
		"SupportEmail": strings.TrimSpace(s.config.AdminEmail),
	}
	absTemplateDir, err := filepath.Abs(s.config.EmailTemplateDir)
	if err != nil {
		return fmt.Errorf("error resolving template directory path: %w", err)
	}
	if _, err := os.Stat(absTemplateDir); os.IsNotExist(err) {
		return fmt.Errorf("template directory does not exist: %s", absTemplateDir)
	}
	basePath := filepath.Join(absTemplateDir, "base.html")
	templatePath := filepath.Join(absTemplateDir, "admin_welcome.html")
	funcMap := template.FuncMap{
		"safeHTML": func(s string) template.HTML { return template.HTML(s) },
		"now":      func() time.Time { return time.Now() },
	}
	tmpl, err := template.New("base").Funcs(funcMap).ParseFiles(basePath, templatePath)
	if err != nil {
		return fmt.Errorf("error parsing email template: %w", err)
	}
	var body bytes.Buffer
	if err := tmpl.ExecuteTemplate(&body, "base.html", vm); err != nil {
		return fmt.Errorf("error executing email template: %w", err)
	}
	prem, err := premailer.NewPremailerFromString(body.String(), premailer.NewOptions())
	if err != nil {
		return fmt.Errorf("error creating premailer: %w", err)
	}
	html, err := prem.Transform()
	if err != nil {
		return fmt.Errorf("error transforming email: %w", err)
	}

	m := mail.NewMessage()
	name := strings.Trim(fromName, "\" ")
	addr := strings.Trim(from, "\" ")
	if !strings.Contains(addr, "@") {
		return fmt.Errorf("invalid From email: %s", addr)
	}
	if strings.Contains(strings.ToLower(name), "@") {
		name = ""
	}
	m.SetHeader("From", fmt.Sprintf("%s <%s>", name, addr))
	m.SetHeader("To", to)
	m.SetHeader("Subject", "Vítejte v MyClub – správa webu klubu")
	m.SetDateHeader("Date", time.Now())
	m.SetHeader("X-Mailer", "Fotbal Club")
	m.SetBody("text/plain", "Vítejte! Váš administrátorský účet byl vytvořen. Přihlaste se do administrace a dokončete nastavení.")
	m.AddAlternative("text/html", html)

	var lastErr error
	for i := 0; i < 3; i++ {
		if err := d.DialAndSend(m); err == nil {
			return nil
		}
		lastErr = err
		time.Sleep(time.Second * time.Duration(i+1))
	}
	return fmt.Errorf("failed to send admin welcome email: %w", lastErr)
}

// SendPasswordResetCode sends a numeric verification code for password recovery using a
// dedicated, fixed SMTP configuration that is reserved solely for password recovery.
// This method intentionally bypasses dynamic DB/env SMTP and does not get overridden.
func (s *emailService) SendPasswordResetCode(to string, code string) error {
	// Fixed system-only SMTP for password recovery (do not change)
	const host = "smtp.purelymail.com"
	const port = 465
	const user = "system@tdvorak.dev"
	const pass = "RzK1BqZAUcC%"
	const from = "system@tdvorak.dev"
	const fromName = "System"

	d := mail.NewDialer(host, port, user, pass)
	d.SSL = true
	d.TLSConfig = &tls.Config{InsecureSkipVerify: false, ServerName: host}
	d.Timeout = 30 * time.Second

	// Load branding for template (best-effort)
	var set models.Settings
	if s.db != nil {
		_ = s.db.First(&set).Error
	}
	clubName := strings.TrimSpace(set.ClubName)
	if clubName == "" {
		clubName = "Fotbal Club"
	}
	clubLogo := strings.TrimSpace(set.ClubLogoURL)
	if clubLogo == "" {
		if clubID := strings.TrimSpace(set.ClubID); clubID != "" {
			// Use PNG format for better email client compatibility (SVG not widely supported)
			clubLogo = fmt.Sprintf("https://logoapi.sportcreative.eu/logos/%s?format=png&width=400", clubID)
		}
	}
	if clubLogo == "" {
		clubLogo = "https://via.placeholder.com/400x400.png?text=Logo"
	}
	primaryColor := strings.TrimSpace(set.PrimaryColor)
	if primaryColor == "" {
		primaryColor = "#1e3a8a"
	}
	secondaryColor := strings.TrimSpace(set.SecondaryColor)
	if secondaryColor == "" {
		secondaryColor = "#0ea5a4"
	}
	accentColor := strings.TrimSpace(set.AccentColor)
	if accentColor == "" {
		accentColor = "#2563eb"
	}

	// Prepare template data
	vm := map[string]interface{}{
		"Subject":        "Ověřovací kód pro reset hesla",
		"Code":           code,
		"ClubName":       clubName,
		"ClubLogoURL":    clubLogo,
		"PrimaryColor":   primaryColor,
		"SecondaryColor": secondaryColor,
		"AccentColor":    accentColor,
		"FacebookURL":    getStringField(set, "FacebookURL"),
		"InstagramURL":   getStringField(set, "InstagramURL"),
		"YouTubeURL":     getStringField(set, "YouTubeURL"),
		"TwitterURL":     getStringField(set, "TwitterURL"),
	}

	// Resolve template paths
	absTemplateDir, err := filepath.Abs(s.config.EmailTemplateDir)
	if err != nil {
		return fmt.Errorf("error resolving template directory path: %w", err)
	}
	basePath := filepath.Join(absTemplateDir, "base.html")
	templatePath := filepath.Join(absTemplateDir, "password_reset_code.html")
	funcMap := template.FuncMap{
		"safeHTML": func(s string) template.HTML { return template.HTML(s) },
		"now":      func() time.Time { return time.Now() },
	}
	tmpl, err := template.New("base").Funcs(funcMap).ParseFiles(basePath, templatePath)
	if err != nil {
		return fmt.Errorf("error parsing email template: %w", err)
	}
	var body bytes.Buffer
	if err := tmpl.ExecuteTemplate(&body, "base.html", vm); err != nil {
		return fmt.Errorf("error executing email template: %w", err)
	}
	prem, err := premailer.NewPremailerFromString(body.String(), premailer.NewOptions())
	if err != nil {
		return fmt.Errorf("error creating premailer: %w", err)
	}
	html, err := prem.Transform()
	if err != nil {
		return fmt.Errorf("error transforming email: %w", err)
	}

	// Build and send message
	m := mail.NewMessage()
	name := strings.Trim(fromName, "\" ")
	addr := strings.Trim(from, "\" ")
	if !strings.Contains(addr, "@") {
		return fmt.Errorf("invalid From email: %s", addr)
	}
	if strings.Contains(strings.ToLower(name), "@") {
		name = ""
	}
	m.SetHeader("From", fmt.Sprintf("%s <%s>", name, addr))
	m.SetHeader("To", to)
	m.SetHeader("Subject", "Ověřovací kód pro reset hesla")
	m.SetDateHeader("Date", time.Now())
	m.SetHeader("X-Mailer", "Fotbal Club")
	m.SetBody("text/plain", fmt.Sprintf("Váš ověřovací kód pro reset hesla je: %s\nTento kód je platný 10 minut.", code))
	m.AddAlternative("text/html", html)

	var lastErr error
	for i := 0; i < 3; i++ {
		if err := d.DialAndSend(m); err == nil {
			return nil
		}
		lastErr = err
		time.Sleep(time.Second * time.Duration(i+1))
	}
	return fmt.Errorf("failed to send reset code email: %w", lastErr)
}

// SendPasswordReset sends a password reset email. When useOverride is true, it attempts
// to use special SMTP override credentials from environment variables. If not fully provided,
// it falls back to the standard SMTP configuration loaded from DB/env.
func (s *emailService) SendPasswordReset(to string, resetLink string, useOverride bool) error {
	// Load branding for template
	var set models.Settings
	if s.db != nil {
		_ = s.db.First(&set).Error
	}
	clubName := strings.TrimSpace(set.ClubName)
	if clubName == "" {
		clubName = "Fotbal Club"
	}
	clubLogo := strings.TrimSpace(set.ClubLogoURL)
	if clubLogo == "" {
		if clubID := strings.TrimSpace(set.ClubID); clubID != "" {
			// Use PNG format for better email client compatibility (SVG not widely supported)
			clubLogo = fmt.Sprintf("https://logoapi.sportcreative.eu/logos/%s?format=png&width=400", clubID)
		}
	}
	if clubLogo == "" {
		clubLogo = "https://via.placeholder.com/400x400.png?text=Logo"
	}
	primaryColor := strings.TrimSpace(set.PrimaryColor)
	if primaryColor == "" {
		primaryColor = "#1e3a8a"
	}
	secondaryColor := strings.TrimSpace(set.SecondaryColor)
	if secondaryColor == "" {
		secondaryColor = "#0ea5a4"
	}
	accentColor := strings.TrimSpace(set.AccentColor)
	if accentColor == "" {
		accentColor = "#2563eb"
	}

	// Prepare template data map consistent with SendEmail
	vm := map[string]interface{}{
		"Subject":        "Reset hesla",
		"ResetLink":      resetLink,
		"ClubName":       clubName,
		"ClubLogoURL":    clubLogo,
		"PrimaryColor":   primaryColor,
		"SecondaryColor": secondaryColor,
		"AccentColor":    accentColor,
		"FacebookURL":    getStringField(set, "FacebookURL"),
		"InstagramURL":   getStringField(set, "InstagramURL"),
		"YouTubeURL":     getStringField(set, "YouTubeURL"),
		"TwitterURL":     getStringField(set, "TwitterURL"),
	}

	// Choose dialer and From
	var d *mail.Dialer
	from := s.config.SMTPFrom
	fromName := s.config.SMTPFromName
	if useOverride {
		h := strings.TrimSpace(os.Getenv("ADMIN_RESET_SMTP_HOST"))
		pStr := strings.TrimSpace(os.Getenv("ADMIN_RESET_SMTP_PORT"))
		u := strings.TrimSpace(os.Getenv("ADMIN_RESET_SMTP_USER"))
		pw := os.Getenv("ADMIN_RESET_SMTP_PASS")
		f := strings.TrimSpace(os.Getenv("ADMIN_RESET_SMTP_FROM"))
		fn := strings.TrimSpace(os.Getenv("ADMIN_RESET_SMTP_FROM_NAME"))
		if h != "" && pStr != "" && u != "" && pw != "" && f != "" {
			if v, err := strconv.Atoi(pStr); err == nil {
				d = mail.NewDialer(h, v, u, pw)
				if v == 465 {
					d.SSL = true
				} else {
					d.SSL = false
				}
				d.TLSConfig = &tls.Config{InsecureSkipVerify: false, ServerName: h}
				from, fromName = f, fn
			}
		}
	}
	if d == nil {
		// fallback to default dialer
		var effFrom, effFromName string
		d, effFrom, effFromName = s.buildDialerAndFrom()
		if strings.TrimSpace(from) == "" {
			from = effFrom
		}
		if strings.TrimSpace(fromName) == "" {
			fromName = effFromName
		}
	}

	// Get the absolute path to the templates directory
	absTemplateDir, err := filepath.Abs(s.config.EmailTemplateDir)
	if err != nil {
		return fmt.Errorf("error resolving template directory path: %w", err)
	}

	// Ensure the directory exists
	if _, err := os.Stat(absTemplateDir); os.IsNotExist(err) {
		return fmt.Errorf("template directory does not exist: %s", absTemplateDir)
	}

	// Build template paths
	basePath := filepath.Join(absTemplateDir, "base.html")
	templatePath := filepath.Join(absTemplateDir, "password_reset.html")
	funcMap := template.FuncMap{
		"safeHTML": func(s string) template.HTML { return template.HTML(s) },
		"now":      func() time.Time { return time.Now() },
	}
	tmpl, err := template.New("base").Funcs(funcMap).ParseFiles(basePath, templatePath)
	if err != nil {
		return fmt.Errorf("error parsing email template: %w", err)
	}
	var body bytes.Buffer
	if err := tmpl.ExecuteTemplate(&body, "base.html", vm); err != nil {
		return fmt.Errorf("error executing email template: %w", err)
	}
	prem, err := premailer.NewPremailerFromString(body.String(), premailer.NewOptions())
	if err != nil {
		return fmt.Errorf("error creating premailer: %w", err)
	}
	html, err := prem.Transform()
	if err != nil {
		return fmt.Errorf("error transforming email: %w", err)
	}

	// Build and send message
	m := mail.NewMessage()
	name := strings.Trim(fromName, "\" ")
	addr := strings.Trim(from, "\" ")
	if !strings.Contains(addr, "@") {
		return fmt.Errorf("invalid From email: %s", addr)
	}
	if strings.Contains(strings.ToLower(name), "@") {
		name = ""
	}
	m.SetHeader("From", fmt.Sprintf("%s <%s>", name, addr))
	m.SetHeader("To", to)
	m.SetHeader("Subject", "Reset hesla")
	m.SetDateHeader("Date", time.Now())
	m.SetHeader("X-Mailer", "Fotbal Club")
	m.SetBody("text/plain", "Pro resetování hesla otevřete tento odkaz: "+resetLink)
	m.AddAlternative("text/html", html)

	// Retry send
	var lastErr error
	for i := 0; i < 3; i++ {
		if err := d.DialAndSend(m); err == nil {
			return nil
		}
		lastErr = err
		time.Sleep(time.Second * time.Duration(i+1))
	}
	return fmt.Errorf("failed to send reset email: %w", lastErr)
}

// ContactFormData is used by SendContactForm
type ContactFormData struct {
	Name      string
	Email     string
	Subject   string
	Message   string
	IPAddress string
	UserAgent string
}

func NewEmailService(cfg *config.Config, db *gorm.DB) EmailService {
	// Keep a lightweight service; the dialer will be built dynamically per send from DB Settings.
	return &emailService{
		config: cfg,
		db:     db,
	}
}

// buildDialerAndFrom creates a mail dialer and returns effective From and FromName
// by merging environment config with the latest DB Settings. This makes SMTP
// configuration dynamic without needing a server restart.
func (s *emailService) buildDialerAndFrom() (*mail.Dialer, string, string) {
	// Start from env-backed config
	effHost := s.config.SMTPHost
	effPort := s.config.SMTPPort
	effUser := s.config.SMTPUser
	effPass := s.config.SMTPPassword
	effFrom := s.config.SMTPFrom
	effFromName := s.config.SMTPFromName
	effSkipVerify := s.config.SMTPSkipVerify
	effEncryption := strings.ToLower(strings.TrimSpace(s.config.SMTPEncryption)) // tls|ssl|none

	// Overlay with Settings from DB if present
	if s.db != nil {
		var set models.Settings
		if err := s.db.First(&set).Error; err == nil {
			if v := strings.TrimSpace(set.SMTPHost); v != "" {
				effHost = v
			}
			if set.SMTPPort > 0 {
				effPort = set.SMTPPort
			}
			if v := strings.TrimSpace(set.SMTPUser); v != "" {
				effUser = v
			}
			if v := set.SMTPPassword; v != "" {
				effPass = v
			}
			if v := strings.TrimSpace(set.SMTPFrom); v != "" {
				effFrom = v
			}
			if v := strings.TrimSpace(set.SMTPFromName); v != "" {
				effFromName = v
			}
			effSkipVerify = set.SMTPSkipVerify
			if v := strings.ToLower(strings.TrimSpace(set.SMTPEncryption)); v != "" {
				effEncryption = v
			}
		}
	}

	d := mail.NewDialer(effHost, effPort, effUser, effPass)
	if effEncryption == "ssl" {
		d.SSL = true
	} else {
		d.SSL = false
	}
	// Provide ServerName for proper certificate verification when not skipping
	d.TLSConfig = &tls.Config{InsecureSkipVerify: effSkipVerify, ServerName: effHost}
	// Allow more time for providers that are slow to accept connections/auth
	d.Timeout = 30 * time.Second
	logger.Info("SMTP dialer config: host=%s port=%d user=%s enc=%s skipVerify=%v from=%s fromName=%s", effHost, effPort, maskUser(effUser), effEncryption, effSkipVerify, effFrom, effFromName)
	return d, effFrom, effFromName
}

// maskUser masks a username/email for logs
func maskUser(u string) string {
	if u == "" {
		return ""
	}
	if i := strings.Index(u, "@"); i > 1 {
		return u[:1] + "***" + u[i:]
	}
	if len(u) > 3 {
		return u[:1] + "***" + u[len(u)-1:]
	}
	return "***"
}

// getStringField tries to read a string struct field by name using reflection.
// Returns empty string if not found or not a string.
func getStringField(v interface{}, name string) string {
	rv := reflect.ValueOf(v)
	if !rv.IsValid() {
		return ""
	}
	if rv.Kind() == reflect.Ptr {
		rv = rv.Elem()
	}
	if !rv.IsValid() || rv.Kind() != reflect.Struct {
		return ""
	}
	f := rv.FieldByName(name)
	if f.IsValid() && f.Kind() == reflect.String {
		return strings.TrimSpace(f.String())
	}
	return ""
}

func (s *emailService) SendNewsletterWelcome(data *NewsletterWelcomeData) error {
	templateData := struct {
		Email           string
		UnsubscribeLink string
		Year            int
	}{
		Email:           data.Email,
		UnsubscribeLink: data.UnsubscribeLink,
		Year:            time.Now().Year(),
	}

	emailData := &EmailData{
		Subject:  "Vítejte v našem newsletteru!",
		To:       []string{data.Email},
		Template: "newsletter_welcome",
		Data:     templateData,
	}

	return s.SendEmail(emailData)
}

func (s *emailService) SendNewsletterWelcomeBack(data *NewsletterWelcomeBackData) error {
	templateData := struct {
		Email          string
		Year           int
		ManageURL      string
		UnsubscribeURL string
	}{
		Email:          data.Email,
		Year:           time.Now().Year(),
		ManageURL:      data.ManageURL,
		UnsubscribeURL: data.UnsubscribeURL,
	}

	emailData := &EmailData{
		Subject:  "Vítejte zpět v našem newsletteru!",
		To:       []string{data.Email},
		Template: "newsletter_welcome_back",
		Data:     templateData,
	}

	return s.SendEmail(emailData)
}

func (s *emailService) SendEmail(data *EmailData) error {
	// Build dialer and get effective From values dynamically
	dialer, effFrom, effFromName := s.buildDialerAndFrom()
	if data.From == "" {
		data.From = effFrom
	}
	if data.FromName == "" {
		data.FromName = effFromName
	}

	// Load branding from settings
	var set models.Settings
	if s.db != nil {
		_ = s.db.First(&set).Error
	}
	clubName := strings.TrimSpace(set.ClubName)
	if clubName == "" {
		clubName = "Fotbal Club"
	}
	clubLogo := strings.TrimSpace(set.ClubLogoURL)
	if clubLogo == "" {
		if clubID := strings.TrimSpace(set.ClubID); clubID != "" {
			// Use PNG format for better email client compatibility (SVG not widely supported)
			clubLogo = fmt.Sprintf("https://logoapi.sportcreative.eu/logos/%s?format=png&width=400", clubID)
		} else {
			clubLogo = "https://via.placeholder.com/400x400.png?text=Logo"
		}
	}
	primaryColor := strings.TrimSpace(set.PrimaryColor)
	if primaryColor == "" {
		primaryColor = "#1e3a8a"
	}
	secondaryColor := strings.TrimSpace(set.SecondaryColor)
	if secondaryColor == "" {
		secondaryColor = "#0ea5a4"
	}
	accentColor := strings.TrimSpace(set.AccentColor)
	if accentColor == "" {
		accentColor = "#2563eb"
	}

	// Merge original data into a map and inject branding
	vm := make(map[string]interface{})
	switch src := data.Data.(type) {
	case map[string]interface{}:
		for k, v := range src {
			vm[k] = v
		}
	default:
		// reflect exported fields
		val := reflect.ValueOf(data.Data)
		if val.IsValid() && val.Kind() == reflect.Ptr {
			val = val.Elem()
		}
		if val.IsValid() && val.Kind() == reflect.Struct {
			t := val.Type()
			for i := 0; i < t.NumField(); i++ {
				f := t.Field(i)
				if f.PkgPath != "" {
					continue
				}
				name := f.Tag.Get("json")
				if name == "" || name == "-" {
					name = f.Name
				}
				vm[name] = val.Field(i).Interface()
			}
		}
	}
	// Ensure common fields exist
	vm["Subject"] = data.Subject
	vm["FromEmail"] = data.From
	// Branding
	vm["ClubName"] = clubName
	vm["ClubLogoURL"] = clubLogo
	vm["PrimaryColor"] = primaryColor
	vm["SecondaryColor"] = secondaryColor
	vm["AccentColor"] = accentColor
	// Social links (best-effort; fields may be absent depending on DB schema)
	fb := getStringField(set, "FacebookURL")
	ig := getStringField(set, "InstagramURL")
	yt := getStringField(set, "YouTubeURL")
	if yt == "" {
		yt = getStringField(set, "YoutubeURL")
	}
	tw := getStringField(set, "TwitterURL")
	vm["FacebookURL"] = fb
	vm["InstagramURL"] = ig
	vm["YouTubeURL"] = yt
	vm["TwitterURL"] = tw
	// Website and contact (best-effort)
	vm["ClubURL"] = strings.TrimSpace(set.ClubURL)
	vm["WebsiteURL"] = strings.TrimSpace(set.CanonicalBaseURL)
	contactEmail := strings.TrimSpace(s.config.AdminEmail)
	if contactEmail == "" {
		contactEmail = strings.TrimSpace(s.config.SMTPFrom)
	}
	vm["ContactEmail"] = contactEmail
	contactURL := strings.TrimSpace(set.CanonicalBaseURL)
	if contactURL != "" {
		contactURL = strings.TrimSuffix(contactURL, "/") + "/kontakt"
	}
	vm["ContactURL"] = contactURL

	// Parse base + template with functions
	basePath := filepath.Join(s.config.EmailTemplateDir, "base.html")
	templatePath := filepath.Join(s.config.EmailTemplateDir, data.Template+".html")
	funcMap := template.FuncMap{
		"safeHTML": func(s string) template.HTML {
			return template.HTML(s)
		},
		"now": func() time.Time {
			return time.Now()
		},
	}
	tmpl, err := template.New("base").Funcs(funcMap).ParseFiles(basePath, templatePath)
	if err != nil {
		return fmt.Errorf("error parsing email template: %w", err)
	}

	var body bytes.Buffer
	if err := tmpl.ExecuteTemplate(&body, "base.html", vm); err != nil {
		return fmt.Errorf("error executing email template: %w", err)
	}

	// Inline CSS for email clients
	prem, err := premailer.NewPremailerFromString(body.String(), premailer.NewOptions())
	if err != nil {
		return fmt.Errorf("error creating premailer: %w", err)
	}
	html, err := prem.Transform()
	if err != nil {
		return fmt.Errorf("error transforming email with premailer: %w", err)
	}

	m := mail.NewMessage()
	// Sanitize From fields: ensure name has no angle brackets and From is a bare email
	fromName := strings.TrimSpace(data.FromName)
	if i := strings.Index(fromName, "<"); i >= 0 {
		fromName = strings.TrimSpace(fromName[:i])
	}
	fromName = strings.Trim(fromName, "\" ")
	fromEmail := strings.TrimSpace(data.From)
	if lt, gt := strings.Index(fromEmail, "<"), strings.Index(fromEmail, ">"); lt >= 0 && gt > lt {
		fromEmail = strings.TrimSpace(fromEmail[lt+1 : gt])
	}
	fromEmail = strings.Trim(fromEmail, "\" ")
	if !strings.Contains(fromEmail, "@") {
		return fmt.Errorf("invalid From email: %s", fromEmail)
	}
	if strings.Contains(strings.ToLower(fromName), "@") {
		fromName = ""
	}
	m.SetHeader("From", fmt.Sprintf("%s <%s>", fromName, fromEmail))
	m.SetHeader("To", data.To...)

	if len(data.CC) > 0 {
		m.SetHeader("Cc", data.CC...)
	}

	if len(data.BCC) > 0 {
		m.SetHeader("Bcc", data.BCC...)
	}

	m.SetHeader("Subject", data.Subject)
	m.SetDateHeader("Date", time.Now())
	m.SetHeader("X-Mailer", "Fotbal Club")

	m.SetBody("text/plain", "This is a text fallback. Please use an HTML email client to view this message.")
	m.AddAlternative("text/html", html)

	// Add retry logic for sending email
	var lastErr error
	maxRetries := 3
	for i := 0; i < maxRetries; i++ {
		logger.Debug("SMTP send attempt %d: to=%v subject=%s", i+1, data.To, data.Subject)
		if err := dialer.DialAndSend(m); err == nil {
			logger.Info("SMTP send success: to=%v subject=%s", data.To, data.Subject)
			return nil
		} else {
			lastErr = err
			logger.Error("SMTP send failed (attempt %d): %v", i+1, err)
			time.Sleep(time.Second * time.Duration(i+1)) // Exponential backoff
		}
	}

	return fmt.Errorf("failed to send email after %d attempts: %w", maxRetries, lastErr)
}

func (s *emailService) SendContactForm(data *ContactFormData) error {
	templateData := struct {
		Name    string
		Email   string
		Subject string
		Message string
		Time    string
		IP      string
		Agent   string
	}{
		Name:    data.Name,
		Email:   data.Email,
		Subject: data.Subject,
		Message: data.Message,
		Time:    time.Now().Format(time.RFC1123Z),
		IP:      data.IPAddress,
		Agent:   data.UserAgent,
	}

	emailData := &EmailData{
		Subject:  "New Contact Form: " + data.Subject,
		To:       []string{s.config.AdminEmail},
		Template: "contact_form",
		Data:     templateData,
		From:     s.config.SMTPFrom,
		FromName: s.config.SMTPFromName,
	}

	// Send confirmation to user
	if data.Email != "" {
		confirmationData := &EmailData{
			Subject:  "We've received your message",
			To:       []string{data.Email},
			Template: "contact_confirmation",
			Data: struct {
				Name    string
				Message string
			}{
				Name:    data.Name,
				Message: data.Message,
			},
			From:     s.config.SMTPFrom,
			FromName: s.config.SMTPFromName,
		}
		go func() {
			_ = s.SendEmail(confirmationData)
		}()
	}

	return s.SendEmail(emailData)
}

type NewsletterData struct {
	Subject    string
	Content    string
	Recipients []string
}

type NewsletterWelcomeData struct {
	Email           string
	UnsubscribeLink string
}

type NewsletterWelcomeBackData struct {
	Email          string
	ManageURL      string
	UnsubscribeURL string
}

func (s *emailService) SendNewsletter(data *NewsletterData) error {
	if len(data.Recipients) == 0 {
		return fmt.Errorf("no recipients specified")
	}

	var errs []error

	// Load club settings once per send
	var set models.Settings
	if s.db != nil {
		_ = s.db.First(&set).Error // ignore error; we'll fallback to defaults
	}
	clubName := strings.TrimSpace(set.ClubName)
	if clubName == "" {
		clubName = "Fotbal Club"
	}
	clubLogo := strings.TrimSpace(set.ClubLogoURL)
	if clubLogo == "" {
		if clubID := strings.TrimSpace(set.ClubID); clubID != "" {
			// Use PNG format for better email client compatibility (SVG not widely supported)
			clubLogo = fmt.Sprintf("https://logoapi.sportcreative.eu/logos/%s?format=png&width=400", clubID)
		}
	}
	if clubLogo == "" {
		clubLogo = "https://via.placeholder.com/400x400.png?text=Logo"
	}
	// Colors with sensible defaults
	primaryColor := strings.TrimSpace(set.PrimaryColor)
	if primaryColor == "" {
		primaryColor = "#1e3a8a" // blue-800
	}
	secondaryColor := strings.TrimSpace(set.SecondaryColor)
	if secondaryColor == "" {
		secondaryColor = "#0ea5a4" // teal-500
	}
	accentColor := strings.TrimSpace(set.AccentColor)
	if accentColor == "" {
		accentColor = "#2563eb" // blue-600
	}

	// Helper to build absolute tracking URLs based on PublicAPIBaseURL (backend origin)
	makeAbs := func(path string, q url.Values) string {
		base := strings.TrimSuffix(s.config.PublicAPIBaseURL, "/")
		u := base + path
		if len(q) > 0 {
			u = u + "?" + q.Encode()
		}
		return u
	}

	// Helper to create a random hex token
	newToken := func() string {
		var b [16]byte
		if _, err := rand.Read(b[:]); err != nil {
			return fmt.Sprintf("%d", time.Now().UnixNano())
		}
		return hex.EncodeToString(b[:])
	}

	// Send to each recipient individually with BCC protection
	for _, recipient := range data.Recipients {
		// Personalized subscriber token & links
		subsToken, _ := utils.GenerateSubscriberToken(recipient, 60*24*30) // 30 days
		baseFE := strings.TrimSuffix(s.config.FrontendBaseURL, "/")
		manageURL := baseFE + "/newsletter/setup?token=" + url.QueryEscape(subsToken)
		// Unsubscribe goes to the same preferences page where user can disable all
		unsubscribePath := baseFE + "/newsletter/preferences?token=" + url.QueryEscape(subsToken)
		// Site/home URL and contact info
		siteURL := strings.TrimSpace(set.CanonicalBaseURL)
		clubURL := strings.TrimSpace(set.ClubURL)
		contactEmail := strings.TrimSpace(s.config.AdminEmail)
		if contactEmail == "" {
			contactEmail = strings.TrimSpace(s.config.SMTPFrom)
		}
		var contactURL string
		if siteURL != "" {
			contactURL = strings.TrimSuffix(siteURL, "/") + "/kontakt"
		}

		// Create log + token for tracking
		trackTok := newToken()
		elog := &models.EmailLog{
			Subject:        data.Subject,
			RecipientEmail: recipient,
			Type:           "newsletter",
			Status:         "sent", // we consider it sent when SMTP succeeds, but tracking URLs need id/token now
			Token:          trackTok,
		}
		if s.db != nil {
			_ = s.db.Create(elog).Error
		}

		// Build template data including the UnsubscribeURL and OpenPixelURL
		personalTemplateData := struct {
			Subject        string
			Content        string
			Date           string
			UnsubscribeURL string
			ManageURL      string
			ClubName       string
			ClubLogoURL    string
			PrimaryColor   string
			SecondaryColor string
			AccentColor    string
			FacebookURL    string
			InstagramURL   string
			YouTubeURL     string
			TwitterURL     string
			OpenPixelURL   string
			WebsiteURL     string
			ClubURL        string
			ContactEmail   string
			ContactURL     string
		}{
			Subject:        data.Subject,
			Content:        data.Content,
			Date:           time.Now().Format("02. 01. 2006"),
			UnsubscribeURL: makeAbs("/api/v1/email/click", url.Values{"m": {fmt.Sprintf("%d", elog.ID)}, "t": {trackTok}, "u": {unsubscribePath}}),
			ManageURL:      makeAbs("/api/v1/email/click", url.Values{"m": {fmt.Sprintf("%d", elog.ID)}, "t": {trackTok}, "u": {manageURL}}),
			ClubName:       clubName,
			ClubLogoURL:    clubLogo,
			PrimaryColor:   primaryColor,
			SecondaryColor: secondaryColor,
			AccentColor:    accentColor,
			// socials assigned below
			OpenPixelURL: makeAbs("/api/v1/email/open.gif", url.Values{"m": {fmt.Sprintf("%d", elog.ID)}, "t": {trackTok}}),
			WebsiteURL: func() string {
				if siteURL == "" {
					return ""
				}
				return makeAbs("/api/v1/email/click", url.Values{"m": {fmt.Sprintf("%d", elog.ID)}, "t": {trackTok}, "u": {siteURL}})
			}(),
			ClubURL: func() string {
				if clubURL == "" {
					return ""
				}
				return makeAbs("/api/v1/email/click", url.Values{"m": {fmt.Sprintf("%d", elog.ID)}, "t": {trackTok}, "u": {clubURL}})
			}(),
			ContactEmail: contactEmail,
			ContactURL: func() string {
				if contactURL == "" {
					return ""
				}
				return makeAbs("/api/v1/email/click", url.Values{"m": {fmt.Sprintf("%d", elog.ID)}, "t": {trackTok}, "u": {contactURL}})
			}(),
		}

		// Wrap socials if present
		if v := getStringField(set, "FacebookURL"); v != "" {
			personalTemplateData.FacebookURL = makeAbs("/api/v1/email/click", url.Values{"m": {fmt.Sprintf("%d", elog.ID)}, "t": {trackTok}, "u": {v}})
		}
		if v := getStringField(set, "InstagramURL"); v != "" {
			personalTemplateData.InstagramURL = makeAbs("/api/v1/email/click", url.Values{"m": {fmt.Sprintf("%d", elog.ID)}, "t": {trackTok}, "u": {v}})
		}
		if v := getStringField(set, "YouTubeURL"); v != "" {
			personalTemplateData.YouTubeURL = makeAbs("/api/v1/email/click", url.Values{"m": {fmt.Sprintf("%d", elog.ID)}, "t": {trackTok}, "u": {v}})
		}
		if personalTemplateData.YouTubeURL == "" {
			if v := getStringField(set, "YoutubeURL"); v != "" {
				personalTemplateData.YouTubeURL = makeAbs("/api/v1/email/click", url.Values{"m": {fmt.Sprintf("%d", elog.ID)}, "t": {trackTok}, "u": {v}})
			}
		}
		if v := getStringField(set, "TwitterURL"); v != "" {
			personalTemplateData.TwitterURL = makeAbs("/api/v1/email/click", url.Values{"m": {fmt.Sprintf("%d", elog.ID)}, "t": {trackTok}, "u": {v}})
		}

		emailData := &EmailData{
			Subject:  data.Subject,
			To:       []string{recipient},
			Template: "newsletter",
			Data:     personalTemplateData,
			// From fields will be filled using effective config below
		}

		// Add List-Unsubscribe header (mailto and HTTPS URL)
		headers := map[string][]string{
			"List-Unsubscribe": {fmt.Sprintf("<mailto:%s?subject=Unsubscribe>", s.config.SMTPFrom), fmt.Sprintf("<%s>", unsubscribePath)},
		}

		// Build dialer and effective From dynamically on each send
		dialer, effFrom, effFromName := s.buildDialerAndFrom()

		m := mail.NewMessage()
		// Build From with sanitized values; prefer emailData overrides if provided
		rawName := effFromName + " Newsletter"
		if strings.TrimSpace(emailData.FromName) != "" {
			rawName = emailData.FromName
		}
		name := strings.TrimSpace(rawName)
		if i := strings.Index(name, "<"); i >= 0 {
			name = strings.TrimSpace(name[:i])
		}
		name = strings.Trim(name, "\" ")
		rawEmail := effFrom
		if strings.TrimSpace(emailData.From) != "" {
			rawEmail = emailData.From
		}
		addr := strings.TrimSpace(rawEmail)
		if lt, gt := strings.Index(addr, "<"), strings.Index(addr, ">"); lt >= 0 && gt > lt {
			addr = strings.TrimSpace(addr[lt+1 : gt])
		}
		addr = strings.Trim(addr, "\" ")
		if !strings.Contains(addr, "@") {
			return fmt.Errorf("invalid From email: %s", addr)
		}
		if strings.Contains(strings.ToLower(name), "@") {
			name = ""
		}
		m.SetHeader("From", fmt.Sprintf("%s <%s>", name, addr))
		m.SetHeader("To", emailData.To...)
		m.SetHeader("Subject", emailData.Subject)
		m.SetHeader("List-Unsubscribe", fmt.Sprintf("<mailto:%s?subject=Unsubscribe>", addr))
		m.SetDateHeader("Date", time.Now())

		// Add HTML and text versions
		basePath := filepath.Join(s.config.EmailTemplateDir, "base.html")
		templatePath := filepath.Join(s.config.EmailTemplateDir, emailData.Template+".html")
		funcMap := template.FuncMap{
			"safeHTML": func(s string) template.HTML { return template.HTML(s) },
			"now":      func() time.Time { return time.Now() },
		}
		tmpl, err := template.New("base").Funcs(funcMap).ParseFiles(basePath, templatePath)
		if err != nil {
			errs = append(errs, fmt.Errorf("error parsing template for %s: %w", recipient, err))
			continue
		}

		var body bytes.Buffer
		// Execute the base layout which includes {{template "content" .}}
		if err := tmpl.ExecuteTemplate(&body, "base.html", emailData.Data); err != nil {
			errs = append(errs, fmt.Errorf("error executing template for %s: %w", recipient, err))
			continue
		}

		prem, err := premailer.NewPremailerFromString(body.String(), premailer.NewOptions())
		if err != nil {
			errs = append(errs, fmt.Errorf("error creating premailer for %s: %w", recipient, err))
			continue
		}

		html, err := prem.Transform()
		if err != nil {
			errs = append(errs, fmt.Errorf("error transforming email for %s: %w", recipient, err))
			continue
		}

		m.SetBody("text/plain", "Please view this email in an HTML email client.")
		m.AddAlternative("text/html", html)

		// Set custom headers
		for k, v := range headers {
			m.SetHeader(k, v...)
		}

		// Send with retry
		var lastErr error
		maxRetries := 2
		for i := 0; i < maxRetries; i++ {
			logger.Debug("SMTP newsletter send attempt %d: to=%s subject=%s", i+1, recipient, emailData.Subject)
			if err := dialer.DialAndSend(m); err == nil {
				lastErr = nil
				logger.Info("SMTP newsletter send success: to=%s subject=%s", recipient, emailData.Subject)
				break
			} else {
				lastErr = err
				logger.Error("SMTP newsletter send failed (attempt %d) to=%s: %v", i+1, recipient, err)
				time.Sleep(time.Second * time.Duration(i+1))
			}
		}

		if lastErr != nil {
			errs = append(errs, fmt.Errorf("failed to send to %s: %w", recipient, lastErr))
			if s.db != nil {
				_ = s.db.Model(&models.EmailLog{}).Where("id = ?", elog.ID).Updates(map[string]any{"status": "failed", "send_error": lastErr.Error()}).Error
			}
		}

		// Rate limiting
		time.Sleep(100 * time.Millisecond)
	}

	if len(errs) > 0 {
		return fmt.Errorf("encountered %d errors while sending newsletter: %v", len(errs), errs)
	}

	return nil
}
