package usecase

import (
	"bytes"
	"context"
	"fmt"
	"html/template"
	"strings"

	"apps/backend/internal/module/notification/domain"

	"github.com/zeromicro/go-zero/core/logx"
)

// Config for notification service.
type Config struct {
	Sender        string
	DevExposeCode bool
	Brand         domain.Brand
	Providers     []domain.EmailDelivery
}

// Service implements domain.UseCase.
type Service struct {
	cfg Config
}

func NewService(cfg Config) *Service {
	if cfg.Sender == "" {
		cfg.Sender = "Harbor Desk <noreply@haixun.local>"
	}
	if cfg.Brand.Name == "" {
		cfg.Brand = domain.DefaultBrand(cfg.Brand.Link)
	}
	if len(cfg.Providers) == 0 {
		cfg.Providers = []domain.EmailDelivery{NewLogDelivery()}
		cfg.DevExposeCode = true
	}
	return &Service{cfg: cfg}
}

func (s *Service) SenderFrom() string       { return s.cfg.Sender }
func (s *Service) ExposeCodeInAPI() bool    { return s.cfg.DevExposeCode }
func (s *Service) Brand() domain.Brand      { return s.cfg.Brand }

func (s *Service) Enabled() bool {
	for _, p := range s.cfg.Providers {
		if p.Name() != "log" {
			return true
		}
	}
	return false
}

func (s *Service) GetEmailTemplate(kind domain.TemplateKind, lang domain.Language) (*domain.EmailTemplate, error) {
	lang = domain.NormalizeLanguage(string(lang))
	return domain.GetTemplate(kind, lang, s.cfg.Brand)
}

func (s *Service) RenderVerify(kind domain.TemplateKind, lang domain.Language, vars domain.VerifyTemplateVars) (string, string, error) {
	lang = domain.NormalizeLanguage(string(lang))
	tpl, err := s.GetEmailTemplate(kind, lang)
	if err != nil {
		return "", "", err
	}
	if vars.Username == "" {
		vars.Username = "there"
	}
	// hermes may leave empty ResetURL button — still OK if placeholder replaced
	t, err := template.New(string(kind)).Parse(tpl.Body)
	if err != nil {
		return "", "", err
	}
	var buf bytes.Buffer
	if err := t.Execute(&buf, vars); err != nil {
		return "", "", err
	}
	return tpl.Subject, buf.String(), nil
}

func (s *Service) SendEmail(ctx context.Context, req domain.EmailRequest) error {
	if req.SenderEmail == "" {
		req.SenderEmail = s.cfg.Sender
	}
	var last error
	for _, p := range s.cfg.Providers {
		if err := p.SendEmail(ctx, req); err != nil {
			logx.WithContext(ctx).Errorf("notification send via %s failed: %v", p.Name(), err)
			last = err
			continue
		}
		logx.WithContext(ctx).Infof("notification sent via %s to %s subject=%q", p.Name(), maskEmail(req.RecipientEmail), req.Subject)
		return nil
	}
	if last != nil {
		return last
	}
	return fmt.Errorf("no email providers configured")
}

func maskEmail(e string) string {
	e = strings.TrimSpace(e)
	at := strings.IndexByte(e, '@')
	if at <= 1 {
		return "***"
	}
	return e[:1] + "***" + e[at:]
}
