package email

import (
	"crypto/sha1"
	"fmt"
	"hash"
	"io"
	"net/mail"
	"sort"

	"github.com/golang/glog"
)

// Hasher is a list of headers that should be considered when hashing an email
// message.
type Hasher []string

// Hash will parse r as an email, and return a hash.Hash that has been applied
// to the values of the headers in h.
func (h Hasher) HashMessage(msg *mail.Message) (hash.Hash, error) {
	// Add deterministic behavior regardless of the order the users specified.
	if !sort.IsSorted(sort.StringSlice(h)) {
		sort.Strings(h)
	}
	hsh := sha1.New()
	for _, header := range h {
		v := msg.Header.Get(header)
		if v == "" {
			glog.V(2).Infoln("Empty", header, "header")
		}
		io.WriteString(hsh, v)
	}
	return hsh, nil
}

func (h Hasher) HashReader(r io.Reader) (hash.Hash, error) {
	msg, err := mail.ReadMessage(r)
	if err != nil {
		return nil, err
	}
	return h.HashMessage(msg)
}

var std = Hasher([]string{"to", "from", "cc", "date", "subject", "message-id"})

// Hash will parse r as an email, and return the hash as a hexadecimal string
// using a default set of headers.
func HashReader(r io.Reader) (string, error) {
	h, err := std.HashReader(r)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", h.Sum(nil)), nil
}

func HashMessage(msg *mail.Message) (string, error) {
	h, err := std.HashMessage(msg)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", h.Sum(nil)), nil
}
