package email

import (
	"context"
	"crypto/tls"
	"fmt"
	"mime"
	"net"
	"net/smtp"
	"strings"
	"time"

	"github.com/nexuscrm/kernel/pkg/ports"
)

const dialTimeout = 10 * time.Second

// SMTPSender delivers email via an SMTP server.
type SMTPSender struct {
	host     string
	port     string
	user     string
	password string
	fromAddr string
	useTLS   bool
}

// NewSMTPSender creates a sender with explicit config.
func NewSMTPSender(host, port, user, password, from string, useTLS bool) *SMTPSender {
	return &SMTPSender{
		host:     host,
		port:     port,
		user:     user,
		password: password,
		fromAddr: from,
		useTLS:   useTLS,
	}
}

// Send delivers the message via SMTP.
func (s *SMTPSender) Send(ctx context.Context, msg *ports.EmailMessage) (*ports.EmailSendResult, error) {
	from := msg.From
	if from == "" {
		from = s.fromAddr
	}

	addr := net.JoinHostPort(s.host, s.port)

	// Collect all recipients.
	var rcpts []string
	rcpts = append(rcpts, msg.To...)
	rcpts = append(rcpts, msg.Cc...)
	rcpts = append(rcpts, msg.Bcc...)
	if len(rcpts) == 0 {
		return nil, fmt.Errorf("email: no recipients specified")
	}

	body := buildMIMEMessage(from, msg)

	// Dial with timeout.
	dialer := net.Dialer{Timeout: dialTimeout}
	conn, err := dialer.DialContext(ctx, "tcp", addr)
	if err != nil {
		return nil, fmt.Errorf("email: dial %s: %w", addr, err)
	}

	c, err := smtp.NewClient(conn, s.host)
	if err != nil {
		conn.Close()
		return nil, fmt.Errorf("email: smtp client: %w", err)
	}
	defer c.Close()

	// STARTTLS when configured and server supports it.
	if s.useTLS {
		if ok, _ := c.Extension("STARTTLS"); ok {
			tlsCfg := &tls.Config{ServerName: s.host}
			if err := c.StartTLS(tlsCfg); err != nil {
				return nil, fmt.Errorf("email: starttls: %w", err)
			}
		}
	}

	// Authenticate if credentials are provided.
	if s.user != "" && s.password != "" {
		auth := smtp.PlainAuth("", s.user, s.password, s.host)
		if err := c.Auth(auth); err != nil {
			return nil, fmt.Errorf("email: auth: %w", err)
		}
	}

	// Envelope.
	if err := c.Mail(from); err != nil {
		return nil, fmt.Errorf("email: MAIL FROM: %w", err)
	}
	for _, rcpt := range rcpts {
		if err := c.Rcpt(rcpt); err != nil {
			return nil, fmt.Errorf("email: RCPT TO failed")
		}
	}

	// Data.
	w, err := c.Data()
	if err != nil {
		return nil, fmt.Errorf("email: DATA: %w", err)
	}
	if _, err := w.Write([]byte(body)); err != nil {
		return nil, fmt.Errorf("email: write body: %w", err)
	}
	if err := w.Close(); err != nil {
		return nil, fmt.Errorf("email: close body: %w", err)
	}

	if err := c.Quit(); err != nil {
		return nil, err
	}
	return ports.AcceptedEmailSendResult(msg, ""), nil
}

// buildMIMEMessage composes the raw RFC 2822 message bytes.
func buildMIMEMessage(from string, msg *ports.EmailMessage) string {
	var b strings.Builder

	b.WriteString("From: " + from + "\r\n")
	b.WriteString("To: " + strings.Join(msg.To, ", ") + "\r\n")
	if len(msg.Cc) > 0 {
		b.WriteString("Cc: " + strings.Join(msg.Cc, ", ") + "\r\n")
	}
	b.WriteString("Subject: " + mime.QEncoding.Encode("utf-8", msg.Subject) + "\r\n")
	b.WriteString("MIME-Version: 1.0\r\n")

	hasPlain := msg.Body != ""
	hasHTML := msg.HTML != ""

	switch {
	case hasHTML && hasPlain:
		boundary := "NexusCRM-boundary-001"
		b.WriteString("Content-Type: multipart/alternative; boundary=" + boundary + "\r\n")
		b.WriteString("\r\n")
		b.WriteString("--" + boundary + "\r\n")
		b.WriteString("Content-Type: text/plain; charset=utf-8\r\n\r\n")
		b.WriteString(msg.Body)
		b.WriteString("\r\n--" + boundary + "\r\n")
		b.WriteString("Content-Type: text/html; charset=utf-8\r\n\r\n")
		b.WriteString(msg.HTML)
		b.WriteString("\r\n--" + boundary + "--\r\n")
	case hasHTML:
		b.WriteString("Content-Type: text/html; charset=utf-8\r\n\r\n")
		b.WriteString(msg.HTML)
	default:
		b.WriteString("Content-Type: text/plain; charset=utf-8\r\n\r\n")
		b.WriteString(msg.Body)
	}

	return b.String()
}
