package sendmail

import (
	"bytes"
	"errors"
	"github.com/emersion/go-message"
	"github.com/emersion/go-message/mail"
	"strings"
	"time"
)

var (
	ErrInvalidBodyType       = errors.New("invalid body type")
	ErrMultipleFromAddresses = errors.New("multiple from addresses")
)

type Json struct {
	From     string `json:"from"`
	ReplyTo  string `json:"reply_to"`
	To       string `json:"to"`
	Cc       string `json:"cc"`
	Bcc      string `json:"bcc"`
	Subject  string `json:"subject"`
	BodyType string `json:"body_type"`
	Body     string `json:"Body"`
}

func (s Json) parseAddresses() (addrFrom, addrReplyTo, addrTo, addrCc, addrBcc []*mail.Address, err error) {
	// parse addresses
	addrFrom, err = mail.ParseAddressList(s.From)
	if err != nil {
		return
	}
	if s.ReplyTo != "" {
		addrReplyTo, err = mail.ParseAddressList(s.ReplyTo)
		if err != nil {
			return
		}
	}
	if s.To != "" {
		addrTo, err = mail.ParseAddressList(s.To)
		if err != nil {
			return
		}
	}
	if s.Cc != "" {
		addrCc, err = mail.ParseAddressList(s.Cc)
		if err != nil {
			return
		}
	}
	if s.Bcc != "" {
		addrBcc, err = mail.ParseAddressList(s.Bcc)
	}
	return
}

func (s Json) PrepareMail(now time.Time) (*Mail, error) {
	// parse addresses from json data
	addrFrom, addrReplyTo, addrTo, addrCc, addrBcc, err := s.parseAddresses()
	if err != nil {
		return nil, err
	}

	// only one from address allowed here
	if len(addrFrom) != 1 {
		return nil, ErrMultipleFromAddresses
	}

	// save for use in the caller
	from := addrFrom[0]

	// set base headers
	var h mail.Header
	h.SetDate(now)
	h.SetSubject(s.Subject)
	h.SetAddressList("From", addrFrom)
	h.SetAddressList("Reply-To", addrReplyTo)
	h.SetAddressList("To", addrTo)
	h.SetAddressList("Cc", addrCc)
	h.SetAddressList("Bcc", addrBcc)

	// set content type header
	switch s.BodyType {
	case "plain":
		h.Set("Content-Type", "text/plain; charset=utf-8")
	case "html":
		h.Set("Content-Type", "text/html; charset=utf-8")
	default:
		return nil, ErrInvalidBodyType
	}

	entity, err := message.New(h.Header, strings.NewReader(s.Body))
	if err != nil {
		return nil, err
	}

	out := new(bytes.Buffer)
	if err := entity.WriteTo(out); err != nil {
		return nil, err
	}

	return &Mail{
		From: from,
		Body: out.Bytes(),
	}, nil
}
