package mailer

import (
	"bytes"
	"errors"
	"io"
	"mime"
	"net/http"
	"net/mail"
	"os/exec"
)

var _ Mailer = (*Sendmail)(nil)

// Sendmail implements `mailer.Mailer` interface and defines a mail
// client that sends emails via the `sendmail` *nix command.
//
// This client is usually recommended only for development and testing.
type Sendmail struct {
}

// Send implements `mailer.Mailer` interface.
//
// Attachments are currently not supported.
func (m *Sendmail) Send(
	fromEmail mail.Address,
	toEmail mail.Address,
	subject string,
	htmlBody string,
	attachments map[string]io.Reader,
) error {
	headers := make(http.Header)
	headers.Set("Subject", mime.QEncoding.Encode("utf-8", subject))
	headers.Set("From", fromEmail.String())
	headers.Set("To", toEmail.String())
	headers.Set("Content-Type", "text/html; charset=UTF-8")

	cmdPath, err := findSendmailPath()
	if err != nil {
		return err
	}

	var buffer bytes.Buffer

	// write
	// ---
	if err := headers.Write(&buffer); err != nil {
		return err
	}
	if _, err := buffer.Write([]byte("\r\n")); err != nil {
		return err
	}
	if _, err := buffer.Write([]byte(htmlBody)); err != nil {
		return err
	}
	// ---

	sendmail := exec.Command(cmdPath, toEmail.Address)
	sendmail.Stdin = &buffer

	return sendmail.Run()
}

func findSendmailPath() (string, error) {
	options := []string{
		"/usr/sbin/sendmail",
		"/usr/bin/sendmail",
		"sendmail",
	}

	for _, option := range options {
		path, err := exec.LookPath(option)
		if err == nil {
			return path, err
		}
	}

	return "", errors.New("Failed to locate a sendmail executable path.")
}
