package email

import (
	"fmt"
	"strings"
	"time"
)

// TemplateData carries the context required to render any notification email.
// Fields are optional per template; callers should populate what they have.
type TemplateData struct {
	ActorName     string
	ActorEmail    string
	DocumentPath  string
	DocumentTitle string
	CommentBody   string
	MentionText   string
	ConflictDesc  string
	ViewURL       string
	UnsubURL      string
	InstanceName  string
}

// Render returns a plain-text subject and body for the given notification
// type. Subjects are short and prefixed with the document path so email
// clients thread per-document.
func Render(kind string, data TemplateData) (subject, body string, err error) {
	if data.InstanceName == "" {
		data.InstanceName = "Cairnquire"
	}
	switch kind {
	case "file_changed":
		return renderFileChanged(data)
	case "comment":
		return renderComment(data)
	case "mention":
		return renderMention(data)
	case "conflict":
		return renderConflict(data)
	default:
		return "", "", fmt.Errorf("unknown email template kind %q", kind)
	}
}

func subjectPrefix(data TemplateData) string {
	if data.DocumentPath != "" {
		return "[" + data.DocumentPath + "]"
	}
	return "[" + data.InstanceName + "]"
}

func footer(data TemplateData) string {
	var b strings.Builder
	b.WriteString("\n--\n")
	if data.ViewURL != "" {
		fmt.Fprintf(&b, "View online: %s\n", data.ViewURL)
	}
	if data.UnsubURL != "" {
		fmt.Fprintf(&b, "Unsubscribe: %s\n", data.UnsubURL)
	}
	fmt.Fprintf(&b, "You receive this because you watch this document on %s.\n", data.InstanceName)
	return b.String()
}

func renderFileChanged(data TemplateData) (string, string, error) {
	title := data.DocumentTitle
	if title == "" {
		title = data.DocumentPath
	}
	actor := data.ActorName
	if actor == "" {
		actor = "Someone"
	}
	subject := fmt.Sprintf("%s Updated by %s", subjectPrefix(data), actor)

	var b strings.Builder
	fmt.Fprintf(&b, "%s updated %q at %s UTC.\n", actor, title, time.Now().UTC().Format("2006-01-02 15:04"))
	if data.DocumentPath != "" {
		fmt.Fprintf(&b, "Path: %s\n", data.DocumentPath)
	}
	b.WriteString("\nView the changes online to see what is new.\n")
	b.WriteString(footer(data))
	return subject, b.String(), nil
}

func renderComment(data TemplateData) (string, string, error) {
	actor := data.ActorName
	if actor == "" {
		actor = "Someone"
	}
	subject := fmt.Sprintf("%s Comment from %s", subjectPrefix(data), actor)

	var b strings.Builder
	fmt.Fprintf(&b, "%s commented on %q:\n", actor, docLabel(data))
	b.WriteString("\n")
	if data.CommentBody != "" {
		// Quote the comment body, line by line, as plain text.
		for _, line := range strings.Split(data.CommentBody, "\n") {
			fmt.Fprintf(&b, "> %s\n", line)
		}
	} else {
		b.WriteString("> (no body)\n")
	}
	b.WriteString("\nOpen the document to reply.\n")
	b.WriteString(footer(data))
	return subject, b.String(), nil
}

func renderMention(data TemplateData) (string, string, error) {
	actor := data.ActorName
	if actor == "" {
		actor = "Someone"
	}
	subject := fmt.Sprintf("%s Mention from %s", subjectPrefix(data), actor)

	var b strings.Builder
	fmt.Fprintf(&b, "%s mentioned you on %q.\n", actor, docLabel(data))
	if data.MentionText != "" {
		b.WriteString("\n")
		for _, line := range strings.Split(data.MentionText, "\n") {
			fmt.Fprintf(&b, "> %s\n", line)
		}
	}
	b.WriteString(footer(data))
	return subject, b.String(), nil
}

func renderConflict(data TemplateData) (string, string, error) {
	subject := fmt.Sprintf("%s Sync conflict", subjectPrefix(data))

	var b strings.Builder
	fmt.Fprintf(&b, "A sync conflict was detected on %q.\n", docLabel(data))
	if data.ConflictDesc != "" {
		fmt.Fprintf(&b, "\nDetails: %s\n", data.ConflictDesc)
	}
	b.WriteString("\nOpen the document to review and resolve the conflict.\n")
	b.WriteString(footer(data))
	return subject, b.String(), nil
}

func docLabel(data TemplateData) string {
	if data.DocumentTitle != "" {
		return data.DocumentTitle
	}
	if data.DocumentPath != "" {
		return data.DocumentPath
	}
	return "a document"
}
