package impl

import (
	"context"
	"crypto/tls"
	"fmt"
	"net/smtp"

	"github.com/jordan-wright/email"

	"github.com/go-sonic/sonic/model/property"
	"github.com/go-sonic/sonic/service"
	"github.com/go-sonic/sonic/util/xerr"
)

type emailServiceImpl struct {
	OptionService service.OptionService
}

func NewEmailService(optionService service.OptionService) service.EmailService {
	return &emailServiceImpl{
		OptionService: optionService,
	}
}

type emailProperties struct {
	Host     string
	Protocol string
	SSLPort  int
	Username string
	Password string
	FromName string
}

func (e *emailServiceImpl) SendTextEmail(ctx context.Context, to, subject, content string) error {
	emailEnable, err := e.OptionService.GetOrByDefaultWithErr(ctx, property.EmailIsEnabled, false)
	if err != nil {
		return err
	}
	if !emailEnable.(bool) {
		return nil
	}
	emailProperties, err := e.getEmailProperties(ctx)
	if err != nil {
		return err
	}
	email := &email.Email{
		To:      []string{to},
		From:    emailProperties.FromName,
		Subject: subject,
		Text:    []byte(content),
	}
	err = e.sendEmail(email, emailProperties)
	return err
}

func (e *emailServiceImpl) SendTemplateEmail(ctx context.Context, to, subject string, content string) error {
	emailEnable, err := e.OptionService.GetOrByDefaultWithErr(ctx, property.EmailIsEnabled, false)
	if err != nil {
		return err
	}
	if !emailEnable.(bool) {
		return nil
	}
	emailProperties, err := e.getEmailProperties(ctx)
	if err != nil {
		return err
	}
	email := &email.Email{
		To:      []string{to},
		From:    emailProperties.FromName,
		Subject: subject,
		HTML:    []byte(content),
	}
	err = e.sendEmail(email, emailProperties)
	return err
}

func (e *emailServiceImpl) TestConnection() error {
	panic("implement me")
}

func (e *emailServiceImpl) getEmailProperties(ctx context.Context) (emailProperties, error) {
	getOrByDefault := func(ctx context.Context, p property.Property, defaultValue interface{}, err error) (interface{}, error) {
		if err != nil {
			return nil, err
		}
		return e.OptionService.GetOrByDefaultWithErr(ctx, p, defaultValue)
	}
	host, err := getOrByDefault(ctx, property.EmailHost, property.EmailHost.DefaultValue, nil)
	protocol, err := getOrByDefault(ctx, property.EmailProtocol, property.EmailProtocol.DefaultValue, err)
	sslPort, err := getOrByDefault(ctx, property.EmailSSLPort, property.EmailSSLPort.DefaultValue, err)
	username, err := getOrByDefault(ctx, property.EmailUsername, property.EmailUsername.DefaultValue, err)
	password, err := getOrByDefault(ctx, property.EmailPassword, property.EmailPassword.DefaultValue, err)
	fromName, err := getOrByDefault(ctx, property.EmailFromName, property.EmailFromName.DefaultValue, err)
	if err != nil {
		return emailProperties{}, err
	}
	emailProperties := emailProperties{
		Host:     host.(string),
		Protocol: protocol.(string),
		SSLPort:  sslPort.(int),
		Username: username.(string),
		Password: password.(string),
		FromName: fromName.(string),
	}
	return emailProperties, nil
}

func (e *emailServiceImpl) sendEmail(email *email.Email, properties emailProperties) error {
	err := email.SendWithTLS(fmt.Sprintf("%s:%d", properties.Host, properties.SSLPort),
		smtp.PlainAuth("", properties.Username, properties.Password, properties.Host), &tls.Config{ServerName: properties.Host, MinVersion: tls.VersionTLS12})
	if err != nil {
		return xerr.Email.Wrapf(err, "发送邮件错误 emailProperties=%v", properties).WithStatus(xerr.StatusInternalServerError).
			WithMsg("发送邮件错误")
	}
	return nil
}