package autoconfig

import (
	"context"
	"encoding/xml"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"
)

const autoconfigTimeout = 4 * time.Second

type mozillaConfig struct {
	XMLName       xml.Name `xml:"clientConfig"`
	EmailProvider struct {
		ID              string `xml:"id,attr"`
		Domain          []string `xml:"domain"`
		DisplayName     string `xml:"displayName"`
		IncomingServers []mozillaServer `xml:"incomingServer"`
		OutgoingServers []mozillaServer `xml:"outgoingServer"`
	} `xml:"emailProvider"`
}

type mozillaServer struct {
	Type       string `xml:"type,attr"`
	Hostname   string `xml:"hostname"`
	Port       int    `xml:"port"`
	SocketType string `xml:"socketType"`
	Username   string `xml:"username"`
}

func fetchMozillaAutoconfig(ctx context.Context, domain string) (Result, bool) {
	urls := []string{
		fmt.Sprintf("https://autoconfig.%s/mail/config-v1.1.xml", domain),
		fmt.Sprintf("https://%s/.well-known/autoconfig/mail/config-v1.1.xml", domain),
		fmt.Sprintf("https://autoconfig.thunderbird.net/v1.1/%s", domain),
	}
	client := &http.Client{Timeout: autoconfigTimeout}
	for _, u := range urls {
		req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
		if err != nil {
			continue
		}
		req.Header.Set("User-Agent", "Ultimail-Autoconfig/1.0")
		resp, err := client.Do(req)
		if err != nil {
			continue
		}
		body, readErr := io.ReadAll(io.LimitReader(resp.Body, 256<<10))
		_ = resp.Body.Close()
		if readErr != nil || resp.StatusCode != http.StatusOK {
			continue
		}
		if r, ok := parseMozillaConfig(body, domain); ok {
			return r, true
		}
	}
	return Result{}, false
}

func parseMozillaConfig(data []byte, domain string) (Result, bool) {
	var cfg mozillaConfig
	if err := xml.Unmarshal(data, &cfg); err != nil {
		return Result{}, false
	}
	var imapHost string
	var imapPort int
	var imapTLS bool
	var smtpHost string
	var smtpPort int
	var smtpTLS bool

	for _, s := range cfg.EmailProvider.IncomingServers {
		if strings.EqualFold(s.Type, "imap") && imapHost == "" {
			imapHost = strings.TrimSpace(s.Hostname)
			imapPort = s.Port
			imapTLS = socketUsesTLS(s.SocketType)
			if imapPort == 0 {
				imapPort = 993
			}
		}
	}
	for _, s := range cfg.EmailProvider.OutgoingServers {
		if strings.EqualFold(s.Type, "smtp") && smtpHost == "" {
			smtpHost = strings.TrimSpace(s.Hostname)
			smtpPort = s.Port
			smtpTLS = socketUsesTLS(s.SocketType)
			if smtpPort == 0 {
				smtpPort = 587
			}
		}
	}
	if imapHost == "" && smtpHost == "" {
		return Result{}, false
	}
	name := strings.TrimSpace(cfg.EmailProvider.DisplayName)
	if name == "" {
		name = cfg.EmailProvider.ID
	}
	if name == "" {
		name = domain
	}
	return Result{
		Domain:       domain,
		ProviderID:   "autoconfig",
		ProviderName: name,
		Source:       "autoconfig",
		Confidence:   "medium",
		IMAPHost:     imapHost,
		IMAPPort:     imapPort,
		IMAPTLS:      imapTLS,
		SMTPHost:     smtpHost,
		SMTPPort:     smtpPort,
		SMTPTLS:      smtpTLS,
		UsernameHint: mozillaUsernameHint(cfg.EmailProvider.IncomingServers),
		AuthMethods:  []string{"password"},
		Notes:        []string{"Configuration issue de l'autoconfiguration Mozilla/Thunderbird."},
	}, true
}

func socketUsesTLS(socketType string) bool {
	switch strings.ToUpper(strings.TrimSpace(socketType)) {
	case "SSL", "TLS", "STARTTLS":
		return true
	default:
		return false
	}
}

func mozillaUsernameHint(servers []mozillaServer) string {
	for _, s := range servers {
		u := strings.ToLower(strings.TrimSpace(s.Username))
		if strings.Contains(u, "%emailaddress%") {
			return "email"
		}
	}
	return "email"
}
