package mq

import (
	"context"
	"encoding/json"
	"log"

	amqp "github.com/rabbitmq/amqp091-go"

	"mengyastore-backend/internal/email"
	"mengyastore-backend/internal/storage"
)

// RunOrderEmailConsumer runs until ctx is done. Must be started in its own goroutine.
func RunOrderEmailConsumer(ctx context.Context, conn *amqp.Connection, env string, site *storage.SiteStore) {
	if conn == nil || site == nil {
		return
	}

	ch, err := conn.Channel()
	if err != nil {
		log.Printf("[MQ] consumer channel: %v", err)
		return
	}
	defer ch.Close()

	if _, _, err := declareTopology(ch, env); err != nil {
		log.Printf("[MQ] consumer declare topology: %v", err)
		return
	}

	if err := ch.Qos(1, 0, false); err != nil {
		log.Printf("[MQ] consumer qos: %v", err)
		return
	}

	queue := QueueName(env)
	const tag = "mengyastore-order-email"
	msgs, err := ch.Consume(queue, tag, false, false, false, false, nil)
	if err != nil {
		log.Printf("[MQ] consume %s: %v", queue, err)
		return
	}

	log.Printf("[MQ] consumer started queue=%s", queue)

	for {
		select {
		case <-ctx.Done():
			_ = ch.Cancel(tag, false)
			log.Printf("[MQ] consumer stopped queue=%s", queue)
			return
		case d, ok := <-msgs:
			if !ok {
				return
			}
			handleDelivery(site, d)
		}
	}
}

func handleDelivery(site *storage.SiteStore, d amqp.Delivery) {
	var payload OrderEmailPayload
	if err := json.Unmarshal(d.Body, &payload); err != nil {
		log.Printf("[MQ] bad message: %v", err)
		_ = d.Ack(false)
		return
	}
	if payload.ToEmail == "" {
		_ = d.Ack(false)
		return
	}

	cfg, err := site.GetSMTPConfig()
	if err != nil || !cfg.IsConfiguredEmail() {
		log.Printf("[MQ] skip email order=%s: smtp not configured", payload.OrderID)
		_ = d.Ack(false)
		return
	}

	emailCfg := email.Config{
		SMTPHost: cfg.Host,
		SMTPPort: cfg.Port,
		From:     cfg.Email,
		Password: cfg.Password,
		FromName: cfg.FromName,
	}
	data := payload.ToNotifyData()
	if err := email.SendOrderNotify(emailCfg, data); err != nil {
		log.Printf("[MQ] send email fail order=%s: %v", payload.OrderID, err)
		if d.Redelivered {
			log.Printf("[MQ] drop order=%s after failed redelivery", payload.OrderID)
			_ = d.Ack(false)
			return
		}
		_ = d.Nack(false, true)
		return
	}
	log.Printf("[MQ] email ok order=%s to=%s", payload.OrderID, payload.ToEmail)
	_ = d.Ack(false)
}
