package email

import (
	"context"
	"fmt"

	emailpkg "{{GO_MODULE}}/pkg/email"
	"{{GO_MODULE}}/pkg/logging"
	"{{GO_MODULE}}/pkg/notify"
	"{{GO_MODULE}}/services/{{COMPONENT_NAME}}/internal/port"
)

// Compile-time interface check.
var _ port.EmailSender = (*NotifySender)(nil)

// NotifySender sends transactional emails via the orchard9 notify service.
// It renders HTML using the email.Renderer before sending so every email
// has a styled layout with inline CSS.
type NotifySender struct {
	client   *notify.Client
	renderer *emailpkg.Renderer
	host     string
	from     string
	logger   *logging.Logger
}

// NewNotifySender creates a notify-backed email sender with HTML rendering.
func NewNotifySender(client *notify.Client, renderer *emailpkg.Renderer, host, from string, logger *logging.Logger) *NotifySender {
	return &NotifySender{
		client:   client,
		renderer: renderer,
		host:     host,
		from:     from,
		logger:   logger.WithComponent("EmailSender"),
	}
}

func (s *NotifySender) SendAuthCode(ctx context.Context, toEmail, code, purpose string) error {
	// Map (purpose, code) to the correct template context.
	emailCtx := purposeToContext(purpose, code)

	rendered, err := s.renderer.Render(purpose, emailCtx)
	if err != nil {
		s.logger.Error("failed to render email template", "purpose", purpose, "error", err)
		return fmt.Errorf("render email %s: %w", purpose, err)
	}
	if rendered.CSSInlineErr != nil {
		s.logger.Warn("CSS inlining failed for email, styles may be degraded in some clients",
			"purpose", purpose, "error", rendered.CSSInlineErr)
	}

	resp, err := s.client.SendEmail(ctx, &notify.SendRequest{
		To:   toEmail,
		From: s.from,
		Content: notify.Content{
			Subject: rendered.Subject,
			HTML:    rendered.HTML,
			Text:    rendered.PlainText,
		},
		Meta: notify.Meta{
			Host:     s.host,
			Category: "critical",
			Tags:     []string{"auth", purpose},
		},
		Options: notify.Options{
			// Stable idempotency key: same user + same code = same key, safe to retry.
			IdempotencyKey: fmt.Sprintf("auth:%s:%s:%s", toEmail, purpose, code),
		},
	})
	if err != nil {
		s.logger.Error("failed to send email via notify", "to", toEmail, "purpose", purpose, "error", err)
		return fmt.Errorf("send email: %w", err)
	}

	s.logger.Info("email queued via notify", "to", toEmail, "purpose", purpose, "message_id", resp.MessageID)
	return nil
}

// purposeToContext maps (purpose, code) to an EmailContext for template rendering.
// code may be an OTP digit string or a URL depending on the purpose.
func purposeToContext(purpose, code string) emailpkg.EmailContext {
	switch purpose {
	case "login_otp":
		return emailpkg.EmailContext{
			Code:      code,
			ExpiresIn: 10,
			Purpose:   "sign in",
		}
	case "magic_link":
		return emailpkg.EmailContext{
			ActionURL:  emailpkg.SafeURL(code),
			ButtonText: "Sign In \u2192",
			ExpiresIn:  15,
		}
	case "password_reset":
		return emailpkg.EmailContext{
			ActionURL:  emailpkg.SafeURL(code),
			ButtonText: "Reset Password \u2192",
			ExpiresIn:  60,
		}
	case "email_verify":
		return emailpkg.EmailContext{
			Code:      code,
			ExpiresIn: 30,
			Purpose:   "verify your email",
		}
	default:
		return emailpkg.EmailContext{
			Code: code,
		}
	}
}
