package main

import (
	"crypto/tls"
	"encoding/base64"
	"flag"
	"fmt"
	"io"
	"net/smtp"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/emersion/go-imap"
	"github.com/emersion/go-imap/client"
)

// Build-time configuration (set via ldflags at compile time)
var (
	BuildIMAPServer   string
	BuildIMAPPort     string
	BuildIMAPUser     string
	BuildIMAPPassword string
	BuildSMTPServer   string
	BuildSMTPPort     string
	BuildSMTPUser     string
	BuildSMTPPassword string
	BuildUseSSL       string
	BuildIgnoreCerts  string
)

// Config holds email connection configuration
type Config struct {
	IMAPServer   string
	IMAPPort     string
	IMAPUser     string
	IMAPPassword string
	SMTPServer   string
	SMTPPort     string
	SMTPUser     string
	SMTPPassword string
	UseSSL       bool
	IgnoreCerts  bool
}

func main() {
	// Parse flags (with build-time defaults)
	imapServer := flag.String("imap-server", BuildIMAPServer, "IMAP server address")
	imapPort := flag.String("imap-port", BuildIMAPPort, "IMAP port (default: 993 for SSL, 143 for STARTTLS)")
	imapUser := flag.String("imap-user", BuildIMAPUser, "IMAP username")
	imapPass := flag.String("imap-pass", BuildIMAPPassword, "IMAP password")
	smtpServer := flag.String("smtp-server", BuildSMTPServer, "SMTP server address")
	smtpPort := flag.String("smtp-port", BuildSMTPPort, "SMTP port (default: 465 for SSL, 587 for STARTTLS)")
	smtpUser := flag.String("smtp-user", BuildSMTPUser, "SMTP username")
	smtpPass := flag.String("smtp-pass", BuildSMTPPassword, "SMTP password")
	operation := flag.String("op", "list-folders", "Operation: list-folders, list-messages, get-message, send-email, delete-messages, move-messages, search")
	folder := flag.String("folder", "INBOX", "IMAP folder to operate on")
	from := flag.String("from", "", "Email sender address (for send-email)")
	to := flag.String("to", "", "Email recipient(s) (comma-separated, for send-email)")
	subject := flag.String("subject", "", "Email subject (for send-email)")
	body := flag.String("body", "", "Email body (for send-email)")
	attachments := flag.String("attachments", "", "Attachment file paths (comma-separated)")
	saveDir := flag.String("save-dir", ".", "Directory to save attachments")
	useSSL := flag.Bool("ssl", BuildUseSSL == "true", "Use SSL/TLS (default: true)")
	ignoreCerts := flag.Bool("ignore-certs", BuildIgnoreCerts == "true", "Ignore certificate validity")
	page := flag.Int("page", 1, "Page number (1-indexed)")
	pageSize := flag.Int("page-size", 25, "Messages per page")
	searchQuery := flag.String("query", "", "Search query (server-side search)")
	uids := flag.String("uids", "", "Message UIDs (comma-separated, for delete/move)")
	destFolder := flag.String("dest-folder", "", "Destination folder (for move-messages)")
	listAttachments := flag.Bool("list-attachments", false, "List message attachments only")
	saveAttachments := flag.Bool("save-attachments", false, "Save message attachments")

	flag.Parse()

	// Build config
	config := Config{
		IMAPServer:   *imapServer,
		IMAPPort:     *imapPort,
		IMAPUser:     *imapUser,
		IMAPPassword: *imapPass,
		SMTPServer:   *smtpServer,
		SMTPPort:     *smtpPort,
		SMTPUser:     *smtpUser,
		SMTPPassword: *smtpPass,
		UseSSL:       *useSSL,
		IgnoreCerts:  *ignoreCerts,
	}

	// Set default ports
	if config.IMAPPort == "" {
		if config.UseSSL {
			config.IMAPPort = "993"
		} else {
			config.IMAPPort = "143"
		}
	}
	if config.SMTPPort == "" {
		if config.UseSSL {
			config.SMTPPort = "465"
		} else {
			config.SMTPPort = "587"
		}
	}

	// Execute operation
	switch *operation {
	case "list-folders":
		err := listFolders(config)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
			os.Exit(1)
		}

	case "list-messages":
		if *folder == "" {
			fmt.Fprintln(os.Stderr, "Error: --folder is required")
			os.Exit(1)
		}
		err := listMessages(config, *folder, *page, *pageSize)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
			os.Exit(1)
		}

	case "get-message":
		if *folder == "" || *uids == "" {
			fmt.Fprintln(os.Stderr, "Error: --folder and --uids are required")
			os.Exit(1)
		}
		err := getMessage(config, *folder, *uids, *listAttachments, *saveAttachments, *saveDir)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
			os.Exit(1)
		}

	case "send-email":
		if *from == "" || *to == "" {
			fmt.Fprintln(os.Stderr, "Error: --from and --to are required")
			os.Exit(1)
		}
		var attachFiles []string
		if *attachments != "" {
			attachFiles = strings.Split(*attachments, ",")
		}
		err := sendEmail(config, *from, strings.Split(*to, ","), *subject, *body, attachFiles)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
			os.Exit(1)
		}
		fmt.Println("Email sent successfully")

	case "delete-messages":
		if *folder == "" || *uids == "" {
			fmt.Fprintln(os.Stderr, "Error: --folder and --uids are required")
			os.Exit(1)
		}
		err := deleteMessages(config, *folder, *uids)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
			os.Exit(1)
		}
		fmt.Println("Messages deleted successfully")

	case "move-messages":
		if *folder == "" || *uids == "" || *destFolder == "" {
			fmt.Fprintln(os.Stderr, "Error: --folder, --uids, and --dest-folder are required")
			os.Exit(1)
		}
		err := moveMessages(config, *folder, *uids, *destFolder)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
			os.Exit(1)
		}
		fmt.Println("Messages moved successfully")

	case "search":
		if *folder == "" || *searchQuery == "" {
			fmt.Fprintln(os.Stderr, "Error: --folder and --query are required")
			os.Exit(1)
		}
		err := searchMessages(config, *folder, *searchQuery)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
			os.Exit(1)
		}

	default:
		fmt.Fprintf(os.Stderr, "Unknown operation: %s\n", *operation)
		os.Exit(1)
	}
}

// connectIMAP establishes a connection to IMAP server
func connectIMAP(config Config) (*client.Client, error) {
	serverAddr := fmt.Sprintf("%s:%s", config.IMAPServer, config.IMAPPort)

	var c *client.Client
	var err error

	if config.UseSSL {
		tlsConfig := &tls.Config{
			InsecureSkipVerify: config.IgnoreCerts,
		}
		c, err = client.DialTLS(serverAddr, tlsConfig)
	} else {
		c, err = client.Dial(serverAddr)
		if err == nil && !config.UseSSL {
			// Try STARTTLS
			tlsConfig := &tls.Config{
				InsecureSkipVerify: config.IgnoreCerts,
			}
			err = c.StartTLS(tlsConfig)
		}
	}

	if err != nil {
		return nil, fmt.Errorf("failed to connect to IMAP server: %w", err)
	}

	// Login
	err = c.Login(config.IMAPUser, config.IMAPPassword)
	if err != nil {
		c.Close()
		return nil, fmt.Errorf("IMAP login failed: %w", err)
	}

	return c, nil
}

// listFolders lists all IMAP folders
func listFolders(config Config) error {
	c, err := connectIMAP(config)
	if err != nil {
		return err
	}
	defer c.Close()

	// List mailboxes
	mailboxes := make(chan *imap.MailboxInfo, 10)
	done := make(chan error, 1)

	go func() {
		done <- c.List("", "*", mailboxes)
	}()

	fmt.Println("IMAP Folders:")
	for m := range mailboxes {
		fmt.Printf("  %s\n", m.Name)
	}

	if err := <-done; err != nil {
		return fmt.Errorf("failed to list folders: %w", err)
	}

	return nil
}

// listMessages lists messages in a folder with pagination
func listMessages(config Config, folder string, page, pageSize int) error {
	c, err := connectIMAP(config)
	if err != nil {
		return err
	}
	defer c.Close()

	// Select mailbox
	mbox, err := c.Select(folder, false)
	if err != nil {
		return fmt.Errorf("failed to select folder %s: %w", folder, err)
	}

	// Calculate message range
	totalMessages := mbox.Messages
	if totalMessages == 0 {
		fmt.Println("No messages in folder")
		return nil
	}

	// Calculate pagination (1-indexed, newest first)
	offset := uint32((page - 1) * pageSize)
	start := totalMessages - offset
	end := start - uint32(pageSize) + 1

	if end < 1 {
		end = 1
	}
	if start > totalMessages {
		start = totalMessages
	}
	if start < end {
		fmt.Println("No messages on this page")
		return nil
	}

	seqset := new(imap.SeqSet)
	seqset.AddRange(start, end)

	// Fetch messages
	messages := make(chan *imap.Message, pageSize)
	done := make(chan error, 1)

	items := []imap.FetchItem{imap.FetchEnvelope, imap.FetchUid}

	go func() {
		done <- c.Fetch(seqset, items, messages)
	}()

	fmt.Printf("Messages in %s (Page %d of %d, showing %d-%d of %d):\n",
		folder, page, (int(totalMessages)+pageSize-1)/pageSize, end, start, totalMessages)
	fmt.Println()

	for msg := range messages {
		fmt.Printf("UID: %d\n", msg.Uid)
		fmt.Printf("  From: %s\n", msg.Envelope.From)
		fmt.Printf("  To: %s\n", msg.Envelope.To)
		if len(msg.Envelope.Subject) > 0 {
			fmt.Printf("  Subject: %s\n", msg.Envelope.Subject)
		} else {
			fmt.Printf("  Subject: (no subject)\n")
		}
		if !msg.Envelope.Date.IsZero() {
			fmt.Printf("  Date: %s\n", msg.Envelope.Date.Format(time.RFC1123))
		}
		fmt.Println()
	}

	if err := <-done; err != nil {
		return fmt.Errorf("failed to fetch messages: %w", err)
	}

	return nil
}

// getMessage retrieves message content
func getMessage(config Config, folder, uidsStr string, listAttachments, saveAttachments bool, saveDir string) error {
	c, err := connectIMAP(config)
	if err != nil {
		return err
	}
	defer c.Close()

	// Parse UIDs
	uidStrings := strings.Split(uidsStr, ",")
	uids := make([]uint32, 0, len(uidStrings))
	for _, uidStr := range uidStrings {
		var uid uint32
		_, err := fmt.Sscanf(strings.TrimSpace(uidStr), "%d", &uid)
		if err != nil {
			return fmt.Errorf("invalid UID %s: %w", uidStr, err)
		}
		uids = append(uids, uid)
	}

	// Select mailbox
	_, err = c.Select(folder, false)
	if err != nil {
		return fmt.Errorf("failed to select folder %s: %w", folder, err)
	}

	// Create save directory if needed
	if saveAttachments {
		err = os.MkdirAll(saveDir, 0755)
		if err != nil {
			return fmt.Errorf("failed to create save directory: %w", err)
		}
	}

	// Fetch messages
	for _, uid := range uids {
		seqset := new(imap.SeqSet)
		seqset.AddNum(uid)

		// Determine what to fetch
		items := []imap.FetchItem{imap.FetchEnvelope, imap.FetchUid}
		if listAttachments || saveAttachments {
			items = append(items, imap.FetchBodyStructure)
		}

		messages := make(chan *imap.Message, 1)
		err := c.UidFetch(seqset, items, messages)
		if err != nil {
			return fmt.Errorf("failed to fetch message %d: %w", uid, err)
		}

		msg := <-messages
		if msg == nil {
			fmt.Printf("Message UID %d not found\n", uid)
			continue
		}

		fmt.Printf("UID: %d\n", uid)
		fmt.Printf("  From: %s\n", msg.Envelope.From)
		fmt.Printf("  Subject: %s\n", msg.Envelope.Subject)

		// Process attachments
		if listAttachments || saveAttachments {
			if msg.BodyStructure == nil {
				fmt.Println("  No body structure available")
				continue
			}

			attachments := extractAttachmentInfo(msg.BodyStructure, "")
			if len(attachments) == 0 {
				fmt.Println("  No attachments found")
			}

			if listAttachments {
				fmt.Printf("  Attachments (%d):\n", len(attachments))
				for i, att := range attachments {
					fmt.Printf("    %d. %s (%s) - Part: %s\n", i+1, att.Filename, att.ContentType, att.PartPath)
				}
			}

			if saveAttachments {
				fmt.Printf("  Saving attachments to %s:\n", saveDir)
				for _, att := range attachments {
					// Fetch the attachment body part
					if att.PartPath == "" {
						fmt.Printf("    Skipping %s: no part path\n", att.Filename)
						continue
					}

					// Parse part path to create BodySectionName
					section := imap.BodySectionName{}
					pathParts := strings.Split(att.PartPath, ".")
					section.Path = make([]int, len(pathParts))
					for i, p := range pathParts {
						_, err := fmt.Sscanf(p, "%d", &section.Path[i])
						if err != nil {
							fmt.Printf("    Error parsing part path for %s: %v\n", att.Filename, err)
							continue
						}
					}

					items := []imap.FetchItem{section.FetchItem()}

					// Fetch this specific message again with the body part
					msgSeqSet := new(imap.SeqSet)
					msgSeqSet.AddNum(uid)

					attachMessages := make(chan *imap.Message, 1)
					err := c.UidFetch(msgSeqSet, items, attachMessages)
					if err != nil {
						fmt.Printf("    Error fetching %s: %v\n", att.Filename, err)
						continue
					}

					attachMsg := <-attachMessages
					if attachMsg == nil {
						fmt.Printf("    Error: attachment not found for %s\n", att.Filename)
						continue
					}

					// Find the literal data for this section
					var reader io.Reader
					for _, r := range attachMsg.Body {
						reader = r
						break
					}

					if reader == nil {
						fmt.Printf("    Error: no data for attachment %s\n", att.Filename)
						continue
					}

					// Read attachment data
					data, err := io.ReadAll(reader)
					if err != nil {
						fmt.Printf("    Error reading %s: %v\n", att.Filename, err)
						continue
					}

					// Save to disk
					safeFilename := filepath.Join(saveDir, att.Filename)
					err = os.WriteFile(safeFilename, data, 0644)
					if err != nil {
						fmt.Printf("    Error saving %s: %v\n", att.Filename, err)
					} else {
						fmt.Printf("    Saved: %s (%d bytes)\n", att.Filename, len(data))
					}
				}
			}
		} else {
			// Print body text - fetch the full message body
			bodyItems := []imap.FetchItem{imap.FetchBody}
			bodySeqSet := new(imap.SeqSet)
			bodySeqSet.AddNum(uid)

			bodyMessages := make(chan *imap.Message, 1)
			err := c.UidFetch(bodySeqSet, bodyItems, bodyMessages)
			if err != nil {
				fmt.Printf("  Error fetching message body: %v\n", err)
			} else {
				bodyMsg := <-bodyMessages
				if bodyMsg != nil {
					for _, section := range bodyMsg.Body {
						reader := section
						_, err := io.Copy(os.Stdout, reader)
						if err != nil {
							fmt.Printf("  Error reading message body: %v\n", err)
						}
					}
				}
			}
		}
		fmt.Println()
	}

	return nil
}

// Attachment represents an email attachment
type Attachment struct {
	Filename    string
	ContentType string
	Data        []byte
	Size        int
	PartPath    string // IMAP part path (e.g., "1.2")
}

// extractAttachmentInfo extracts attachment information from BodyStructure
func extractAttachmentInfo(bs *imap.BodyStructure, partPath string) []Attachment {
	var attachments []Attachment

	if bs == nil {
		return attachments
	}

	// Build part path for this part
	var currentPath string
	if partPath == "" {
		// Root part doesn't have a path in IMAP
		currentPath = ""
	} else {
		currentPath = partPath
	}

	// Check if this part is an attachment
	filename := bs.DispositionParams["filename"]
	if filename == "" {
		filename = bs.Params["name"]
	}

	if filename != "" && (strings.HasPrefix(bs.Disposition, "attachment") || 
	    strings.HasPrefix(bs.MIMEType, "application/") || 
	    strings.HasPrefix(bs.MIMEType, "image/")) {
		// This is an attachment
		attachments = append(attachments, Attachment{
			Filename:    filename,
			ContentType: bs.MIMEType,
			PartPath:    currentPath,
		})
	}

	// Recursively process multipart
	if len(bs.Parts) > 0 {
		for i, part := range bs.Parts {
			var subPath string
			if currentPath == "" {
				subPath = fmt.Sprintf("%d", i+1)
			} else {
				subPath = fmt.Sprintf("%s.%d", currentPath, i+1)
			}
			attachments = append(attachments, extractAttachmentInfo(part, subPath)...)
		}
	}

	return attachments
}

// extractAttachments extracts attachments from a message (deprecated, use extractAttachmentInfo)
func extractAttachments(msg *imap.Message) ([]Attachment, error) {
	return []Attachment{}, nil // Deprecated
}

// deleteMessages deletes messages by UID
func deleteMessages(config Config, folder, uidsStr string) error {
	c, err := connectIMAP(config)
	if err != nil {
		return err
	}
	defer c.Close()

	// Select mailbox
	mbox, err := c.Select(folder, false)
	if err != nil {
		return fmt.Errorf("failed to select folder %s: %w", folder, err)
	}

	// Check if folder is read-only
	if mbox.ReadOnly {
		return fmt.Errorf("folder is read-only")
	}

	// Parse UIDs
	uidStrings := strings.Split(uidsStr, ",")
	seqset := new(imap.SeqSet)
	for _, uidStr := range uidStrings {
		var uid uint32
		_, err := fmt.Sscanf(strings.TrimSpace(uidStr), "%d", &uid)
		if err != nil {
			return fmt.Errorf("invalid UID %s: %w", uidStr, err)
		}
		seqset.AddNum(uid)
	}

	// Delete messages
	if err := c.Store(seqset, imap.FormatFlagsOp(imap.AddFlags, true), []interface{}{imap.DeletedFlag}, nil); err != nil {
		return fmt.Errorf("failed to mark messages for deletion: %w", err)
	}

	if err := c.Expunge(nil); err != nil {
		return fmt.Errorf("failed to expunge messages: %w", err)
	}

	return nil
}

// moveMessages moves messages between folders
func moveMessages(config Config, folder, uidsStr, destFolder string) error {
	c, err := connectIMAP(config)
	if err != nil {
		return err
	}
	defer c.Close()

	// Parse UIDs
	uidStrings := strings.Split(uidsStr, ",")
	uids := make([]uint32, 0, len(uidStrings))
	for _, uidStr := range uidStrings {
		var uid uint32
		_, err := fmt.Sscanf(strings.TrimSpace(uidStr), "%d", &uid)
		if err != nil {
			return fmt.Errorf("invalid UID %s: %w", uidStr, err)
		}
		uids = append(uids, uid)
	}

	// Select source mailbox
	mbox, err := c.Select(folder, false)
	if err != nil {
		return fmt.Errorf("failed to select folder %s: %w", folder, err)
	}

	// Check if folder is read-only
	if mbox.ReadOnly {
		return fmt.Errorf("folder is read-only")
	}

	// Check for MOVE capability
	caps, err := c.Capability()
	if err != nil {
		return fmt.Errorf("failed to get capabilities: %w", err)
	}
	if caps["MOVE"] {
		// Use MOVE extension if available
		seqset := new(imap.SeqSet)
		for _, uid := range uids {
			seqset.AddNum(uid)
		}
		if err := c.UidMove(seqset, destFolder); err != nil {
			return fmt.Errorf("failed to move messages: %w", err)
		}
	} else {
		// Fallback: copy then delete
		seqset := new(imap.SeqSet)
		for _, uid := range uids {
			seqset.AddNum(uid)
		}

		// Copy to destination
		if err := c.UidCopy(seqset, destFolder); err != nil {
			return fmt.Errorf("failed to copy messages: %w", err)
		}

		// Mark as deleted and expunge
		if err := c.Store(seqset, imap.FormatFlagsOp(imap.AddFlags, true), []interface{}{imap.DeletedFlag}, nil); err != nil {
			return fmt.Errorf("failed to mark messages for deletion: %w", err)
		}

		if err := c.Expunge(nil); err != nil {
			return fmt.Errorf("failed to expunge messages: %w", err)
		}
	}

	return nil
}

// searchMessages performs server-side search
func searchMessages(config Config, folder, query string) error {
	c, err := connectIMAP(config)
	if err != nil {
		return err
	}
	defer c.Close()

	// Select mailbox
	_, err = c.Select(folder, false)
	if err != nil {
		return fmt.Errorf("failed to select folder %s: %w", folder, err)
	}

	// Build search criteria (simple keyword search for now)
	criteria := imap.NewSearchCriteria()
	criteria.Text = []string{query}

	ids, err := c.Search(criteria)
	if err != nil {
		return fmt.Errorf("search failed: %w", err)
	}

	if len(ids) == 0 {
		fmt.Printf("No messages matching query: %s\n", query)
		return nil
	}

	fmt.Printf("Found %d messages matching '%s' in %s:\n", len(ids), query, folder)

	// Fetch results
	seqset := new(imap.SeqSet)
	seqset.AddNum(ids...)

	messages := make(chan *imap.Message, len(ids))
	done := make(chan error, 1)

	items := []imap.FetchItem{imap.FetchEnvelope, imap.FetchUid}

	go func() {
		done <- c.Fetch(seqset, items, messages)
	}()

	for msg := range messages {
		fmt.Printf("UID: %d\n", msg.Uid)
		fmt.Printf("  From: %s\n", msg.Envelope.From)
		if len(msg.Envelope.Subject) > 0 {
			fmt.Printf("  Subject: %s\n", msg.Envelope.Subject)
		} else {
			fmt.Printf("  Subject: (no subject)\n")
		}
		if !msg.Envelope.Date.IsZero() {
			fmt.Printf("  Date: %s\n", msg.Envelope.Date.Format(time.RFC1123))
		}
		fmt.Println()
	}

	if err := <-done; err != nil {
		return fmt.Errorf("failed to fetch search results: %w", err)
	}

	return nil
}

// sendEmail sends an email via SMTP
func sendEmail(config Config, from string, to []string, subject, body string, attachments []string) error {
	smtpAddr := fmt.Sprintf("%s:%s", config.SMTPServer, config.SMTPPort)

	// Build message
	var msg strings.Builder
	msg.WriteString(fmt.Sprintf("From: %s\n", from))
	msg.WriteString(fmt.Sprintf("To: %s\n", strings.Join(to, ", ")))
	msg.WriteString(fmt.Sprintf("Subject: %s\n", subject))
	msg.WriteString("MIME-Version: 1.0\n")

	if len(attachments) == 0 {
		msg.WriteString("Content-Type: text/plain; charset=utf-8\n")
		msg.WriteString("\n")
		msg.WriteString(body)
	} else {
		// Multipart with attachments
		boundary := "NextcloudMail_" + fmt.Sprintf("%d", time.Now().UnixNano())
		msg.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=\"%s\"\n", boundary))
		msg.WriteString("\n")

		// Text body
		msg.WriteString(fmt.Sprintf("--%s\n", boundary))
		msg.WriteString("Content-Type: text/plain; charset=utf-8\n")
		msg.WriteString("\n")
		msg.WriteString(body)
		msg.WriteString("\n")

		// Attachments
		for _, file := range attachments {
			data, err := os.ReadFile(file)
			if err != nil {
				return fmt.Errorf("failed to read attachment %s: %w", file, err)
			}

			filename := filepath.Base(file)
			msg.WriteString(fmt.Sprintf("--%s\n", boundary))
			msg.WriteString("Content-Type: application/octet-stream\n")
			msg.WriteString(fmt.Sprintf("Content-Disposition: attachment; filename=\"%s\"\n", filename))
			msg.WriteString("Content-Transfer-Encoding: base64\n")
			msg.WriteString("\n")
			msg.WriteString(encodeBase64(data))
			msg.WriteString("\n")
		}

		msg.WriteString(fmt.Sprintf("--%s--\n", boundary))
	}

	// Connect and send
	var auth smtp.Auth
	if config.SMTPUser != "" && config.SMTPPassword != "" {
		auth = smtp.PlainAuth("", config.SMTPUser, config.SMTPPassword, config.SMTPServer)
	}

	if config.UseSSL {
		tlsConfig := &tls.Config{
			InsecureSkipVerify: config.IgnoreCerts,
			ServerName:         config.SMTPServer,
		}

		// Connect via TLS
		conn, err := tls.Dial("tcp", smtpAddr, tlsConfig)
		if err != nil {
			return fmt.Errorf("failed to connect via TLS: %w", err)
		}
		defer conn.Close()

		client, err := smtp.NewClient(conn, config.SMTPServer)
		if err != nil {
			return fmt.Errorf("failed to create SMTP client: %w", err)
		}
		defer client.Quit()

		if auth != nil {
			if err := client.Auth(auth); err != nil {
				return fmt.Errorf("SMTP authentication failed: %w", err)
			}
		}

		if err := client.Mail(from); err != nil {
			return fmt.Errorf("failed to set sender: %w", err)
		}

		for _, addr := range to {
			if err := client.Rcpt(addr); err != nil {
				return fmt.Errorf("failed to add recipient %s: %w", addr, err)
			}
		}

		wc, err := client.Data()
		if err != nil {
			return fmt.Errorf("failed to send data: %w", err)
		}
		defer wc.Close()

		_, err = wc.Write([]byte(msg.String()))
		if err != nil {
			return fmt.Errorf("failed to write message: %w", err)
		}
	} else {
		// Non-SSL connection with STARTTLS
		err := smtp.SendMail(smtpAddr, auth, from, to, []byte(msg.String()))
		if err != nil {
			return fmt.Errorf("failed to send email: %w", err)
		}
	}

	return nil
}

// encodeBase64 encodes data to base64 with line wrapping
func encodeBase64(data []byte) string {
	const maxLineLen = 76
	encoded := base64.StdEncoding.EncodeToString(data)
	
	// Add line breaks every maxLineLen characters
	var result strings.Builder
	for i := 0; i < len(encoded); i += maxLineLen {
		end := i + maxLineLen
		if end > len(encoded) {
			end = len(encoded)
		}
		result.WriteString(encoded[i:end])
		result.WriteString("\n")
	}
	
	return result.String()
}
