package mail

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"

	"github.com/jackc/pgx/v5"
	"github.com/ultisuite/ulti-backend/internal/mail/listunsubscribe"
	"github.com/ultisuite/ulti-backend/internal/mail/smtp"
)

// MailSender sends immediately without outbox persistence.
type MailSender interface {
	Send(ctx context.Context, req *smtp.SendRequest) error
}

var (
	ErrListUnsubscribeUnavailable = errors.New("list-unsubscribe mailto not available")
	ErrListUnsubscribeNoMailto    = errors.New("list-unsubscribe has no mailto target")
)

type messageAuthInfo struct {
	ListUnsubscribe string `json:"list_unsubscribe"`
}

// SendMailtoListUnsubscribe sends the RFC 2369 mailto unsubscribe without outbox or sent copy.
func (s *Service) SendMailtoListUnsubscribe(
	ctx context.Context,
	externalID, messageID string,
	sender MailSender,
) (*listunsubscribe.Mailto, error) {
	if sender == nil {
		return nil, errors.New("mail sender not configured")
	}

	var accountID string
	var authRaw []byte
	err := s.db.QueryRow(ctx, `
		SELECT m.account_id, m.auth_info
		FROM messages m
		JOIN mail_accounts ma ON m.account_id = ma.id
		WHERE m.id = $1 AND ma.user_id = (SELECT id FROM users WHERE external_id = $2)
	`, messageID, externalID).Scan(&accountID, &authRaw)
	if err != nil {
		if errors.Is(err, pgx.ErrNoRows) {
			return nil, ErrNotFound
		}
		return nil, err
	}

	var auth messageAuthInfo
	if len(authRaw) > 0 {
		_ = json.Unmarshal(authRaw, &auth)
	}
	parsed := listunsubscribe.Parse(auth.ListUnsubscribe)
	if parsed.Mailto == nil {
		if parsed.HTTP != "" {
			return nil, fmt.Errorf("%w: use http url", ErrListUnsubscribeNoMailto)
		}
		return nil, ErrListUnsubscribeUnavailable
	}

	fromEmail, err := s.resolveAccountFromEmail(ctx, accountID)
	if err != nil {
		return nil, err
	}

	m := parsed.Mailto
	req := &smtp.SendRequest{
		AccountID: accountID,
		From:      fromEmail,
		To:        []string{m.Address},
		Subject:   m.Subject,
		BodyText:  m.Body,
	}
	if err := sender.Send(ctx, req); err != nil {
		return nil, err
	}
	return m, nil
}

func (s *Service) resolveAccountFromEmail(ctx context.Context, accountID string) (string, error) {
	var fromEmail string
	err := s.db.QueryRow(ctx, `
		SELECT mi.email FROM mail_identities mi
		JOIN mail_accounts ma ON mi.account_id = ma.id
		WHERE ma.id = $1 AND mi.is_default = true
		LIMIT 1
	`, accountID).Scan(&fromEmail)
	if err == nil && fromEmail != "" {
		return fromEmail, nil
	}
	if err := s.db.QueryRow(ctx, `SELECT email FROM mail_accounts WHERE id = $1`, accountID).Scan(&fromEmail); err != nil {
		return "", err
	}
	if fromEmail == "" {
		return "", errors.New("account has no from address")
	}
	return fromEmail, nil
}
