package imap

import (
	"crypto/tls"
	"fmt"
	"net"
	"strings"
	"time"

	imapv2 "github.com/emersion/go-imap/v2"
	"github.com/emersion/go-imap/v2/imapclient"
)

const (
	dialTimeout  = 30 * time.Second
	fetchTimeout = 5 * time.Minute // per-batch read/write deadline
)

// Conn wraps an IMAP client with the underlying net.Conn so callers
// can set per-operation deadlines to prevent indefinite blocking.
type Conn struct {
	*imapclient.Client
	raw net.Conn
}

// SetFetchDeadline sets a 5-minute read/write deadline on the connection.
// Call this before each fetch batch to prevent stalled imports.
func (c *Conn) SetFetchDeadline() {
	_ = c.raw.SetDeadline(time.Now().Add(fetchTimeout))
}

// ClearDeadline removes any active deadline from the underlying connection.
func (c *Conn) ClearDeadline() {
	_ = c.raw.SetDeadline(time.Time{})
}

// junkTrashNames lists well-known junk/trash folder names for fallback detection.
var junkTrashNames = []string{
	"junk", "spam", "trash", "deleted items",
	"deleted messages", "papierkorb", "gelöschte elemente",
}

// FolderInfo describes a single IMAP folder with exclusion metadata.
type FolderInfo struct {
	Name     string `json:"name"`
	Excluded bool   `json:"excluded"`
	Reason   string `json:"reason,omitempty"`
}

// Connect establishes an IMAP client connection using the specified TLS mode.
// Returns a Conn that exposes the underlying net.Conn for deadline management.
func Connect(host string, port int, tlsMode string) (*Conn, error) {
	addr := fmt.Sprintf("%s:%d", host, port)

	switch tlsMode {
	case "ssl":
		dialer := &tls.Dialer{
			NetDialer: &net.Dialer{Timeout: dialTimeout},
			Config:    &tls.Config{ServerName: host},
		}
		raw, err := dialer.Dial("tcp", addr)
		if err != nil {
			return nil, fmt.Errorf("imap connect ssl: %w", err)
		}
		c := imapclient.New(raw, nil)
		return &Conn{Client: c, raw: raw}, nil

	case "starttls":
		raw, err := net.DialTimeout("tcp", addr, dialTimeout)
		if err != nil {
			return nil, fmt.Errorf("imap connect starttls: %w", err)
		}
		c := imapclient.New(raw, &imapclient.Options{
			TLSConfig: &tls.Config{ServerName: host},
		})
		return &Conn{Client: c, raw: raw}, nil

	case "none":
		raw, err := net.DialTimeout("tcp", addr, dialTimeout)
		if err != nil {
			return nil, fmt.Errorf("imap connect plain: %w", err)
		}
		c := imapclient.New(raw, nil)
		return &Conn{Client: c, raw: raw}, nil

	default:
		return nil, fmt.Errorf("imap: unknown tls mode %q", tlsMode)
	}
}

// ListFolders retrieves all mailbox folders and detects junk/trash folders.
func ListFolders(c *imapclient.Client) ([]FolderInfo, error) {
	listCmd := c.List("", "*", nil)
	mailboxes, err := listCmd.Collect()
	if err != nil {
		return nil, fmt.Errorf("imap list folders: %w", err)
	}

	var folders []FolderInfo
	for _, mb := range mailboxes {
		fi := FolderInfo{Name: mb.Mailbox}

		// Check special-use attributes (RFC 6154)
		for _, attr := range mb.Attrs {
			if attr == imapv2.MailboxAttrJunk {
				fi.Excluded = true
				fi.Reason = "special_use"
				break
			}
			if attr == imapv2.MailboxAttrTrash {
				fi.Excluded = true
				fi.Reason = "special_use"
				break
			}
		}

		// Fallback: case-insensitive name matching
		if !fi.Excluded {
			lower := strings.ToLower(mb.Mailbox)
			for _, jt := range junkTrashNames {
				if lower == jt {
					fi.Excluded = true
					fi.Reason = "name_match"
					break
				}
			}
		}

		folders = append(folders, fi)
	}

	return folders, nil
}
