package telegram

import (
	"fmt"
	"html"
	"strings"
)

type parseMode string

const (
	parseModeUnset      parseMode = ""
	parseModeMarkdown   parseMode = "Markdown"
	parseModeMarkdownV2 parseMode = "MarkdownV2"
	parseModeHTML       parseMode = "HTML"
)

func normalizeParseMode(value string) parseMode {
	switch strings.ToLower(strings.TrimSpace(value)) {
	case "markdown":
		return parseModeMarkdown
	case "markdownv2":
		return parseModeMarkdownV2
	case "html":
		return parseModeHTML
	default:
		return parseModeUnset
	}
}

func (pm parseMode) String() string {
	return string(pm)
}

type messageField struct {
	label string
	value string
}

type messageTemplate struct {
	title     string
	fields    []messageField
	emphasize []string
}

func (mt messageTemplate) Format(mode parseMode) string {
	var builder strings.Builder
	builder.WriteString(formatTitle(mode, mt.title, mt.emphasize))
	builder.WriteString("\n")
	builder.WriteString("-----------------------------\n")

	formatter := selectValueFormatter(mode)
	for _, field := range mt.fields {
		appendMessageField(&builder, field.label, field.value, formatter)
	}
	return builder.String()
}

type valueFormatter func(string) string

func formatTitle(mode parseMode, title string, emphasize []string) string {
	switch mode {
	case parseModeMarkdown:
		return highlightMarkdown(title, emphasize, escapeMarkdown)
	case parseModeMarkdownV2:
		return highlightMarkdown(title, emphasize, escapeMarkdownV2)
	case parseModeHTML:
		return highlightHTML(title, emphasize)
	default:
		return title
	}
}

func highlightMarkdown(title string, emphasize []string, esc func(string) string) string {
	if len(emphasize) == 0 {
		return title
	}
	result := title
	for _, word := range emphasize {
		word = strings.TrimSpace(word)
		if word == "" {
			continue
		}
		escaped := esc(word)
		replacement := fmt.Sprintf("*%s*", escaped)
		result = strings.ReplaceAll(result, word, replacement)
	}
	return result
}

func highlightHTML(title string, emphasize []string) string {
	if len(emphasize) == 0 {
		return html.EscapeString(title)
	}
	result := html.EscapeString(title)
	for _, word := range emphasize {
		word = strings.TrimSpace(word)
		if word == "" {
			continue
		}
		escaped := html.EscapeString(word)
		replacement := fmt.Sprintf("<b>%s</b>", escaped)
		result = strings.ReplaceAll(result, escaped, replacement)
	}
	return result
}

func appendMessageField(builder *strings.Builder, label, value string, formatter valueFormatter) {
	value = strings.TrimSpace(value)
	if value == "" {
		value = "—"
	} else if formatter != nil {
		value = formatter(value)
	}
	fmt.Fprintf(builder, "• %s: %s\n", label, value)
}

func selectValueFormatter(mode parseMode) valueFormatter {
	switch mode {
	case parseModeMarkdown:
		return func(value string) string {
			return fmt.Sprintf("*%s*", escapeMarkdown(value))
		}
	case parseModeMarkdownV2:
		return func(value string) string {
			return fmt.Sprintf("*%s*", escapeMarkdownV2(value))
		}
	case parseModeHTML:
		return func(value string) string {
			return fmt.Sprintf("<b>%s</b>", html.EscapeString(value))
		}
	default:
		return nil
	}
}

var markdownEscaper = strings.NewReplacer(
	"*", "\\*",
	"_", "\\_",
	"[", "\\[",
	"]", "\\]",
	"(", "\\(",
	")", "\\)",
	"`", "\\`",
)

var markdownV2Escaper = strings.NewReplacer(
	"_", "\\_",
	"*", "\\*",
	"[", "\\[",
	"]", "\\]",
	"(", "\\(",
	")", "\\)",
	"~", "\\~",
	"`", "\\`",
	">", "\\>",
	"#", "\\#",
	"+", "\\+",
	"-", "\\-",
	"=", "\\=",
	"|", "\\|",
	"{", "\\{",
	"}", "\\}",
	".", "\\.",
	"!", "\\!",
)

func escapeMarkdown(value string) string {
	return markdownEscaper.Replace(value)
}

func escapeMarkdownV2(value string) string {
	return markdownV2Escaper.Replace(value)
}
