package template

import (
	"bytes"
	"fmt"
	htmltemplate "html/template"
	"strings"
	texttemplate "text/template"

	"gateway/internal/model/notification/domain/enum"
	domtpl "gateway/internal/model/notification/domain/template"
)

// Renderer renders notification templates from a registry.
type Renderer struct {
	registry  domtpl.Registry
	fallbacks []string
}

// NewRenderer builds a Renderer. Implements domain/template.Renderer.
func NewRenderer(registry domtpl.Registry, fallbacks ...string) *Renderer {
	if registry == nil {
		registry = DefaultRegistry()
	}
	return &Renderer{
		registry:  registry,
		fallbacks: fallbacks,
	}
}

// Render produces subject/body/sms text for the given kind and locale.
func (r *Renderer) Render(kind enum.NotifyKind, locale string, data map[string]any) (*domtpl.Rendered, error) {
	spec, err := r.registry.Lookup(kind, locale, r.fallbacks...)
	if err != nil {
		return nil, err
	}

	if err := validateRequiredVars(spec.RequiredVars, data); err != nil {
		return nil, err
	}

	subjectTpl, bodyTpl, smsTpl, err := resolveSpec(spec)
	if err != nil {
		return nil, err
	}

	out := &domtpl.Rendered{}
	if subjectTpl != "" {
		subject, err := executeTextTemplate("subject", subjectTpl, data)
		if err != nil {
			return nil, fmt.Errorf("template: render email subject: %w", err)
		}
		out.Subject = subject
	}
	if bodyTpl != "" {
		body, err := executeHTMLTemplate("body", bodyTpl, data)
		if err != nil {
			return nil, fmt.Errorf("template: render email body: %w", err)
		}
		out.Body = body
	}
	if smsTpl != "" {
		sms, err := executeTextTemplate("sms", smsTpl, data)
		if err != nil {
			return nil, fmt.Errorf("template: render sms: %w", err)
		}
		out.SMSText = sms
	}

	if out.Subject == "" && out.Body == "" && out.SMSText == "" {
		return nil, fmt.Errorf("template: kind %q has no renderable fields for locale %q", kind, locale)
	}

	return out, nil
}

func validateRequiredVars(required []string, data map[string]any) error {
	for _, key := range required {
		if _, ok := data[key]; !ok {
			return fmt.Errorf("template: missing required variable %q", key)
		}
	}
	return nil
}

func executeTextTemplate(name, tpl string, data map[string]any) (string, error) {
	t, err := texttemplate.New(name).Parse(tpl)
	if err != nil {
		return "", err
	}
	var buf bytes.Buffer
	if err := t.Execute(&buf, data); err != nil {
		return "", err
	}
	return strings.TrimSpace(buf.String()), nil
}

func executeHTMLTemplate(name, tpl string, data map[string]any) (string, error) {
	t, err := htmltemplate.New(name).Parse(tpl)
	if err != nil {
		return "", err
	}
	var buf bytes.Buffer
	if err := t.Execute(&buf, data); err != nil {
		return "", err
	}
	return strings.TrimSpace(buf.String()), nil
}
