package identity

import (
	"context"
	"crypto/tls"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"html"
	"net"
	"net/smtp"
	"strconv"
	"strings"

	"gorm.io/gorm"

	"qycw/go-server/internal/config"
	"qycw/go-server/internal/plugins"
)

type emailConfig struct {
	Enabled       bool
	Provider      string
	Host          string
	Port          int
	User          string
	Password      string
	FromEmail     string
	FromName      string
	Security      string
	AuthEnabled   bool
	ValidateCerts bool
	Timeout       int
	ReplyTo       string
}

type emailContent struct {
	TextBody     string
	HTMLBody     string
	Template     string
	TemplateData map[string]any
}

func sendEmail(ctx context.Context, db *gorm.DB, cfg config.Bootstrap, pluginSvc *plugins.Service, to string, subject string, body string) error {
	return sendEmailContent(ctx, db, cfg, pluginSvc, to, subject, emailContent{TextBody: body})
}

func sendEmailContent(ctx context.Context, db *gorm.DB, cfg config.Bootstrap, pluginSvc *plugins.Service, to string, subject string, content emailContent) error {
	emailCfg, err := loadEmailConfig(ctx, db, cfg)
	if err != nil {
		return err
	}
	if !emailCfg.Enabled {
		return BadRequest("邮件服务未启用")
	}
	if plugins.PluginIDFromProvider(emailCfg.Provider) == "" && (emailCfg.Host == "" || emailCfg.FromEmail == "") {
		return ServiceUnavailable("邮件服务未配置完整")
	}

	if pluginID := plugins.PluginIDFromProvider(emailCfg.Provider); pluginID != "" {
		if pluginSvc == nil {
			return ServiceUnavailable("邮件插件运行时未启用")
		}
		resp, err := pluginSvc.SendEmail(ctx, pluginID, plugins.EmailSendRequest{
			To:           to,
			Subject:      subject,
			TextBody:     content.TextBody,
			HTMLBody:     content.HTMLBody,
			Template:     content.Template,
			TemplateData: content.TemplateData,
			Config:       pluginSvc.MergeConfig(ctx, pluginID, emailPluginConfig(emailCfg)),
		})
		if err != nil {
			return ServiceUnavailable("邮件插件发送失败：" + err.Error())
		}
		if !resp.OK {
			message := strings.TrimSpace(resp.Message)
			if message == "" {
				message = "邮件插件发送失败"
			}
			return ServiceUnavailable(message)
		}
		return nil
	}

	addr := net.JoinHostPort(emailCfg.Host, fmt.Sprint(emailCfg.Port))
	fromName := emailCfg.FromName
	if fromName == "" {
		fromName = cfg.AppName
	}
	body := content.TextBody
	contentType := "text/plain"
	if strings.TrimSpace(content.HTMLBody) != "" {
		body = content.HTMLBody
		contentType = "text/html"
	}
	headers := []string{
		fmt.Sprintf("From: %s <%s>", encodeHeader(fromName), emailCfg.FromEmail),
		fmt.Sprintf("To: <%s>", to),
		fmt.Sprintf("Subject: %s", encodeHeader(subject)),
		"MIME-Version: 1.0",
		"Content-Type: " + contentType + "; charset=UTF-8",
	}
	if emailCfg.ReplyTo != "" {
		headers = append(headers, fmt.Sprintf("Reply-To: <%s>", emailCfg.ReplyTo))
	}
	message := strings.Join(append(headers, "", body), "\r\n")

	var auth smtp.Auth
	if emailCfg.AuthEnabled && emailCfg.User != "" {
		auth = smtp.PlainAuth("", emailCfg.User, emailCfg.Password, emailCfg.Host)
	}
	tlsCfg := &tls.Config{
		ServerName:         emailCfg.Host,
		InsecureSkipVerify: !emailCfg.ValidateCerts,
	}
	switch emailCfg.Security {
	case "ssl":
		return sendEmailSSL(addr, emailCfg, auth, tlsCfg, to, []byte(message))
	case "starttls":
		return sendEmailStartTLS(addr, emailCfg, auth, tlsCfg, to, []byte(message))
	case "none":
		return smtp.SendMail(addr, auth, emailCfg.FromEmail, []string{to}, []byte(message))
	default:
		return BadRequest("SMTP_SECURITY must be one of: ssl, starttls, none")
	}
}

func emailPluginConfig(cfg emailConfig) map[string]any {
	return map[string]any{
		"smtp_host":           cfg.Host,
		"smtp_port":           cfg.Port,
		"smtp_username":       cfg.User,
		"smtp_password":       cfg.Password,
		"smtp_from":           cfg.FromEmail,
		"smtp_from_name":      cfg.FromName,
		"smtp_security":       cfg.Security,
		"smtp_auth_enabled":   cfg.AuthEnabled,
		"smtp_validate_certs": cfg.ValidateCerts,
		"smtp_timeout":        cfg.Timeout,
		"smtp_reply_to":       cfg.ReplyTo,
	}
}

func (s *Service) sendWelcomeEmail(ctx context.Context, user User, passwordSet bool) error {
	if user.Email == nil || strings.TrimSpace(*user.Email) == "" {
		return nil
	}
	baseURL := s.frontendURL(ctx)
	username := strings.TrimSpace(user.Username)
	email := strings.TrimSpace(*user.Email)
	textBody := welcomeEmailBody(username, email, passwordSet, baseURL)
	templateData := map[string]any{"username": username, "email": email, "password_set": passwordSet, "login_url": strings.TrimRight(baseURL, "/") + "/login"}
	return sendEmailContent(ctx, s.db, s.cfg, s.plugins, email, "【清云互联】注册成功，欢迎加入", emailContent{
		TextBody:     textBody,
		HTMLBody:     welcomeEmailHTML(username, email, passwordSet, baseURL),
		Template:     "identity.welcome",
		TemplateData: templateData,
	})
}

func (s *Service) frontendURL(ctx context.Context) string {
	return strings.TrimRight(s.settingString(ctx, "platform.frontend_url", "http://localhost:5173"), "/")
}

func welcomeEmailBody(username string, email string, passwordSet bool, baseURL string) string {
	if strings.TrimSpace(username) == "" {
		username = email
	}
	lines := []string{
		fmt.Sprintf("%s，您好：", username),
		"",
		"您已成功注册清云互联账户。",
		fmt.Sprintf("  用户名：%s", username),
		fmt.Sprintf("  邮箱：%s", email),
		"",
	}
	if passwordSet {
		lines = append(lines, "您可以使用注册时设置的密码登录。")
	} else {
		lines = append(lines,
			"您注册时未设置密码，目前仅可使用邮箱验证码方式登录。",
			"如需启用密码登录，请登录后在“账户设置”中设置一个新密码。",
		)
	}
	lines = append(lines,
		fmt.Sprintf("登录入口：%s/login", strings.TrimRight(baseURL, "/")),
		"",
		"—— 清云互联团队",
	)
	return strings.Join(lines, "\n")
}

func welcomeEmailHTML(username string, email string, passwordSet bool, baseURL string) string {
	if strings.TrimSpace(username) == "" {
		username = email
	}
	loginURL := strings.TrimRight(baseURL, "/") + "/login"
	passwordNotice := "您可以使用注册时设置的密码登录。"
	if !passwordSet {
		passwordNotice = "您注册时未设置密码，目前可使用邮箱验证码登录；登录后可在账户设置中设置密码。"
	}
	return fmt.Sprintf(`<!doctype html><html><body style="margin:0;background:#f3f5f7;font-family:Arial,'Microsoft YaHei',sans-serif;color:#17212b"><table role="presentation" width="100%%" cellspacing="0" cellpadding="0"><tr><td align="center" style="padding:32px 16px"><table role="presentation" width="100%%" cellspacing="0" cellpadding="0" style="max-width:620px;background:#fff;border:1px solid #dfe5ea"><tr><td style="padding:24px 28px;background:#17212b;color:#fff;font-size:20px;font-weight:700">清云互联</td></tr><tr><td style="padding:30px 28px"><h1 style="margin:0 0 18px;font-size:24px">欢迎加入清云互联</h1><p style="margin:0 0 20px;line-height:1.8">%s，您好。您的账户已经注册成功。</p><table role="presentation" width="100%%" cellspacing="0" cellpadding="0" style="background:#f7f9fa;border:1px solid #e4e9ed"><tr><td style="padding:12px 16px;color:#5b6873;width:88px">用户名</td><td style="padding:12px 16px;font-weight:600">%s</td></tr><tr><td style="padding:12px 16px;color:#5b6873;border-top:1px solid #e4e9ed">邮箱</td><td style="padding:12px 16px;border-top:1px solid #e4e9ed">%s</td></tr></table><p style="margin:20px 0;line-height:1.8">%s</p><a href="%s" style="display:inline-block;padding:12px 20px;background:#1677ff;color:#fff;text-decoration:none;font-weight:600">登录控制台</a><p style="margin:28px 0 0;color:#7a8792;font-size:13px;line-height:1.7">如非本人操作，请立即联系平台管理员。</p></td></tr></table></td></tr></table></body></html>`, html.EscapeString(username), html.EscapeString(username), html.EscapeString(email), html.EscapeString(passwordNotice), html.EscapeString(loginURL))
}

func loadEmailConfig(ctx context.Context, db *gorm.DB, cfg config.Bootstrap) (emailConfig, error) {
	out := emailConfig{
		Enabled:       cfg.SMTPHost != "" && cfg.SMTPFromEmail != "",
		Provider:      "mode.email.aliyunemail.provider:create_provider",
		Host:          cfg.SMTPHost,
		Port:          cfg.SMTPPort,
		User:          cfg.SMTPUser,
		Password:      cfg.SMTPPassword,
		FromEmail:     cfg.SMTPFromEmail,
		FromName:      cfg.SMTPFromName,
		Security:      "ssl",
		AuthEnabled:   true,
		ValidateCerts: !cfg.SMTPInsecureSkipTLSVerify,
		Timeout:       15,
	}
	if out.Port == 0 {
		out.Port = 465
	}
	if db == nil {
		return out, nil
	}
	var settings []SystemSetting
	if err := db.WithContext(ctx).Where("`key` LIKE ?", "notification.email.%").Find(&settings).Error; err != nil {
		return out, err
	}
	for _, item := range settings {
		switch item.Key {
		case "notification.email.enabled":
			out.Enabled = item.Value.Bool(false)
		case "notification.email.provider":
			out.Provider = settingString(item.Value, out.Provider)
		case "notification.email.smtp_host":
			out.Host = settingString(item.Value, "")
		case "notification.email.smtp_port":
			out.Port = settingInt(item.Value, out.Port)
		case "notification.email.smtp_user":
			out.User = settingString(item.Value, "")
		case "notification.email.smtp_password":
			out.Password = settingString(item.Value, "")
		case "notification.email.smtp_from_email":
			out.FromEmail = settingString(item.Value, "")
		case "notification.email.smtp_from_name":
			out.FromName = settingString(item.Value, "QingYun Cloud")
		case "notification.email.smtp_security":
			out.Security = strings.ToLower(settingString(item.Value, out.Security))
		case "notification.email.smtp_auth_enabled":
			out.AuthEnabled = item.Value.Bool(true)
		case "notification.email.smtp_validate_certs":
			out.ValidateCerts = item.Value.Bool(true)
		case "notification.email.smtp_timeout":
			out.Timeout = settingInt(item.Value, out.Timeout)
		case "notification.email.smtp_reply_to":
			out.ReplyTo = settingString(item.Value, "")
		}
	}
	if strings.Contains(out.Provider, "aliyunemail") {
		out.Security = "ssl"
		out.AuthEnabled = true
	}
	if out.Security == "" {
		out.Security = "ssl"
	}
	if out.FromName == "" {
		out.FromName = "QingYun Cloud"
	}
	return out, nil
}

func sendEmailSSL(addr string, cfg emailConfig, auth smtp.Auth, tlsCfg *tls.Config, to string, message []byte) error {
	conn, err := tls.Dial("tcp", addr, tlsCfg)
	if err != nil {
		return err
	}
	defer conn.Close()
	client, err := smtp.NewClient(conn, cfg.Host)
	if err != nil {
		return err
	}
	defer client.Close()
	return sendWithClient(client, cfg, auth, to, message)
}

func sendEmailStartTLS(addr string, cfg emailConfig, auth smtp.Auth, tlsCfg *tls.Config, to string, message []byte) error {
	client, err := smtp.Dial(addr)
	if err != nil {
		return err
	}
	defer client.Close()
	if ok, _ := client.Extension("STARTTLS"); ok {
		if err := client.StartTLS(tlsCfg); err != nil {
			return err
		}
	}
	return sendWithClient(client, cfg, auth, to, message)
}

func sendWithClient(client *smtp.Client, cfg emailConfig, auth smtp.Auth, to string, message []byte) error {
	if cfg.AuthEnabled && auth != nil {
		if err := client.Auth(auth); err != nil {
			return err
		}
	}
	if err := client.Mail(cfg.FromEmail); err != nil {
		return err
	}
	if err := client.Rcpt(to); err != nil {
		return err
	}
	writer, err := client.Data()
	if err != nil {
		return err
	}
	if _, err := writer.Write(message); err != nil {
		_ = writer.Close()
		return err
	}
	if err := writer.Close(); err != nil {
		return err
	}
	return client.Quit()
}

func settingInt(raw SettingValue, fallback int) int {
	if len(raw) == 0 {
		return fallback
	}
	var decoded any
	if err := json.Unmarshal(raw, &decoded); err == nil {
		switch value := decoded.(type) {
		case float64:
			return int(value)
		case string:
			parsed, err := strconv.Atoi(strings.TrimSpace(value))
			if err == nil {
				return parsed
			}
		}
	}
	text := strings.Trim(strings.TrimSpace(string(raw)), `"`)
	parsed, err := strconv.Atoi(text)
	if err != nil {
		return fallback
	}
	return parsed
}

func encodeHeader(value string) string {
	return "=?UTF-8?B?" + base64.StdEncoding.EncodeToString([]byte(value)) + "?="
}
