package googleapi

import (
	"gogs.mikescher.com/BlackForestBytes/goext/langext"
	"mime"
	"strings"
	"time"
)

// https://datatracker.ietf.org/doc/html/rfc2822
func encodeMimeMail(from string, recipients []string, cc []string, bcc []string, subject string, body MailBody, attachments []MailAttachment) string {

	data := make([]string, 0, 32)

	data = append(data, "Date: "+time.Now().Format(time.RFC1123Z))
	data = append(data, "MIME-Version: 1.0")
	data = append(data, "From: "+mime.QEncoding.Encode("UTF-8", from))
	data = append(data, "To: "+strings.Join(langext.ArrMap(recipients, func(v string) string { return mime.QEncoding.Encode("UTF-8", v) }), ", "))
	if len(cc) > 0 {
		data = append(data, "To: "+strings.Join(langext.ArrMap(cc, func(v string) string { return mime.QEncoding.Encode("UTF-8", v) }), ", "))
	}
	if len(bcc) > 0 {
		data = append(data, "Bcc: "+strings.Join(langext.ArrMap(bcc, func(v string) string { return mime.QEncoding.Encode("UTF-8", v) }), ", "))
	}
	data = append(data, "Subject: "+mime.QEncoding.Encode("UTF-8", subject))

	hasInlineAttachments := langext.ArrAny(attachments, func(v MailAttachment) bool { return v.IsInline })
	hasNormalAttachments := langext.ArrAny(attachments, func(v MailAttachment) bool { return !v.IsInline })
	hasPlain := body.Plain != ""
	hasHTML := body.HTML != ""

	mixedBoundary := langext.MustRawHexUUID()
	relatedBoundary := langext.MustRawHexUUID()
	altBoundary := langext.MustRawHexUUID()

	inlineAttachments := langext.ArrFilter(attachments, func(v MailAttachment) bool { return v.IsInline })
	normalAttachments := langext.ArrFilter(attachments, func(v MailAttachment) bool { return !v.IsInline })

	if hasInlineAttachments && hasNormalAttachments {
		// "mixed+related"

		data = append(data, "Content-Type: multipart/mixed; boundary="+mixedBoundary)
		data = append(data, "")
		data = append(data, "--"+mixedBoundary)

		data = append(data, "Content-Type: multipart/related; boundary="+relatedBoundary)
		data = append(data, "")

		data = append(data, dumpMailBody(body, hasInlineAttachments, hasNormalAttachments, relatedBoundary, altBoundary)...)
		data = append(data, "")

		for i, attachment := range inlineAttachments {
			data = append(data, "--"+relatedBoundary)
			data = append(data, attachment.dump()...)

			if i < len(inlineAttachments)-1 {
				data = append(data, "")
			}
		}

		data = append(data, "--"+relatedBoundary+"--")

		for i, attachment := range normalAttachments {
			data = append(data, "--"+mixedBoundary)
			data = append(data, attachment.dump()...)

			if i < len(normalAttachments)-1 {
				data = append(data, "")
			}
		}

		data = append(data, "--"+mixedBoundary+"--")

	} else if hasNormalAttachments {
		// "mixed"

		data = append(data, "Content-Type: multipart/mixed; boundary="+mixedBoundary)
		data = append(data, "")

		data = append(data, dumpMailBody(body, hasInlineAttachments, hasNormalAttachments, mixedBoundary, altBoundary)...)
		if hasPlain && hasHTML {
			data = append(data, "")
		}

		for i, attachment := range normalAttachments {
			data = append(data, "--"+mixedBoundary)
			data = append(data, attachment.dump()...)

			if i < len(normalAttachments)-1 {
				data = append(data, "")
			}
		}

		data = append(data, "--"+mixedBoundary+"--")

	} else if hasInlineAttachments {
		// "related"

		data = append(data, "Content-Type: multipart/related; boundary="+relatedBoundary)
		data = append(data, "")

		data = append(data, dumpMailBody(body, hasInlineAttachments, hasNormalAttachments, relatedBoundary, altBoundary)...)
		data = append(data, "")

		for i, attachment := range inlineAttachments {
			data = append(data, "--"+relatedBoundary)
			data = append(data, attachment.dump()...)

			if i < len(inlineAttachments)-1 {
				data = append(data, "")
			}
		}

		data = append(data, "--"+relatedBoundary+"--")

	} else if hasPlain && hasHTML {
		// "alternative"

		data = append(data, "Content-Type: multipart/alternative; boundary="+altBoundary)
		data = append(data, "")

		data = append(data, dumpMailBody(body, hasInlineAttachments, hasNormalAttachments, altBoundary, altBoundary)...)
		data = append(data, "")

		data = append(data, "--"+altBoundary+"--")

	} else if hasPlain {
		// "plain"

		data = append(data, "Content-Type: text/plain; charset=UTF-8")
		data = append(data, "Content-Transfer-Encoding: 7bit")
		data = append(data, "")
		data = append(data, body.Plain)

	} else if hasHTML {
		// "plain"

		data = append(data, "Content-Type: text/html; charset=UTF-8")
		data = append(data, "Content-Transfer-Encoding: 7bit")
		data = append(data, "")
		data = append(data, body.HTML)

	} else {
		// "empty??"

	}

	return strings.Join(data, "\r\n")
}

func dumpMailBody(body MailBody, hasInlineAttachments bool, hasNormalAttachments bool, boundary string, boundaryAlt string) []string {

	if body.HTML != "" && body.Plain != "" && !hasInlineAttachments && hasNormalAttachments {
		data := make([]string, 0, 16)
		data = append(data, "--"+boundary)
		data = append(data, "Content-Type: multipart/alternative; boundary="+boundaryAlt)
		data = append(data, "")
		data = append(data, "--"+boundaryAlt)
		data = append(data, "Content-Type: text/plain; charset=UTF-8")
		data = append(data, "Content-Transfer-Encoding: 7bit")
		data = append(data, "")
		data = append(data, body.Plain)
		data = append(data, "")
		data = append(data, "--"+boundaryAlt)
		data = append(data, "Content-Type: text/html; charset=UTF-8")
		data = append(data, "Content-Transfer-Encoding: 7bit")
		data = append(data, "")
		data = append(data, body.HTML)
		data = append(data, "")
		data = append(data, "--"+boundaryAlt+"--")
		return data
	}

	if body.HTML != "" && body.Plain != "" && hasInlineAttachments {
		data := make([]string, 0, 2)
		data = append(data, "--"+boundary)
		data = append(data, body.HTML)
		return data
	}

	if body.HTML != "" && body.Plain != "" {
		data := make([]string, 0, 8)
		data = append(data, "--"+boundary)
		data = append(data, "Content-Type: text/plain; charset=UTF-8")
		data = append(data, "Content-Transfer-Encoding: 7bit")
		data = append(data, "")
		data = append(data, body.Plain)
		data = append(data, "")
		data = append(data, "--"+boundary)
		data = append(data, "Content-Type: text/html; charset=UTF-8")
		data = append(data, "Content-Transfer-Encoding: 7bit")
		data = append(data, "")
		data = append(data, body.HTML)
		return data
	}

	if body.HTML != "" {
		data := make([]string, 0, 2)
		data = append(data, "--"+boundary)
		data = append(data, "Content-Type: text/html; charset=UTF-8")
		data = append(data, "Content-Transfer-Encoding: 7bit")
		data = append(data, "")
		data = append(data, body.HTML)
		return data
	}

	if body.Plain != "" {
		data := make([]string, 0, 2)
		data = append(data, "--"+boundary)
		data = append(data, "Content-Type: text/plain; charset=UTF-8")
		data = append(data, "Content-Transfer-Encoding: 7bit")
		data = append(data, "")
		data = append(data, body.Plain)
		return data
	}

	data := make([]string, 0, 16)
	data = append(data, "--"+boundary)
	data = append(data, "Content-Type: text/plain; charset=UTF-8")
	data = append(data, "Content-Transfer-Encoding: 7bit")
	data = append(data, "")
	data = append(data, "") // no content ?!?
	return data
}