package logic

import (
	"bytes"
	"fmt"
	"fusenapi/utils/check"
	"fusenapi/utils/fstpl"
	"html/template"
	"log"
	"net/smtp"
	"sync"
	"time"

	"github.com/zeromicro/go-zero/core/logx"
)

var EmailTaskResendTime = time.Second * 30
var TimeLimit *check.TimeLimit[string]
var EmailManager *EmailSender

var tpls *template.Template

func init() {
	var err error

	tpls = fstpl.AutoParseTplFiles()
	if err != nil {
		log.Fatal(err)
	}

	TimeLimit = check.NewTimeLimit[string](EmailTaskResendTime)

	// Initialize the email manager
	EmailManager = &EmailSender{
		EmailTasks: make(chan *EmailFormat, 10),
		Auth: smtp.PlainAuth(
			"fusen support",
			"support@fusenpack.com",
			"wfbjpdgvaozjvwah",
			"smtp.gmail.com",
		),
		FromEmail:       "support@fusenpack.com",
		emailSending:    make(map[string]*EmailTask, 10),
		ResendTimeLimit: EmailTaskResendTime,
		semaphore:       make(chan struct{}, 100), // Initialize semaphore with a capacity of 10
	}

	// Start processing email tasks
	go EmailManager.ProcessEmailTasks()

	// Start clearing expired tasks
	go EmailManager.ClearExpiredTasks()
}

type EmailFormat struct {
	TemplateName     string            // 模板名字
	UniqueKey        string            // 用于处理唯一的任务,重发都会被利用到
	TargetEmail      string            // 发送的目标email
	CompanyName      string            // fs公司名
	ConfirmationLink string            // fs确认连接
	SenderName       string            // 发送人
	SenderTitle      string            // 发送标题
	Extend           map[string]string // 扩展参数
}

// 验证邮件格式是否符合要求
func (eformat *EmailFormat) Vaild() error {

	// 1. 检查模板名称
	if tpls.Lookup(eformat.TemplateName) == nil {
		return fmt.Errorf("%s template name is not found", eformat.TemplateName)
	}

	// 2. 检查公司名称
	if eformat.CompanyName == "" {
		return fmt.Errorf("company name cannot be empty")
	}

	// 3. 检查确认链接
	if eformat.ConfirmationLink == "" {
		return fmt.Errorf("confirmation link cannot be empty")
	}

	// 4. 检查发送人名称
	if eformat.SenderName == "" {
		return fmt.Errorf("sender name cannot be empty")
	}

	// 5. 检查发送人头衔
	if eformat.SenderTitle == "" {
		return fmt.Errorf("sender title cannot be empty")
	}

	// 6. 检查目标邮箱
	if eformat.TargetEmail == "" {
		return fmt.Errorf("target email cannot be empty")
	}

	// 7. 检查唯一键
	if eformat.UniqueKey == "" {
		return fmt.Errorf("unique key cannot be empty")
	}

	// 所有校验通过
	return nil

}

// EmailSender
type EmailSender struct {
	lock            sync.Mutex
	EmailTasks      chan *EmailFormat
	Auth            smtp.Auth
	FromEmail       string
	ResendTimeLimit time.Duration

	emailSending map[string]*EmailTask
	semaphore    chan struct{}
}

// EmailTask
type EmailTask struct {
	Email    *EmailFormat // email
	SendTime time.Time    // 处理的任务时间
}

// ProcessEmailTasks 是 EmailSender 结构体的方法,用于处理邮件任务。
func (m *EmailSender) ProcessEmailTasks() {
	for {
		// 从 EmailTasks 通道中接收邮件任务
		emailformat, ok := <-m.EmailTasks
		if !ok {
			log.Println("Email task channel closed")
			break
		}

		// 验证邮件格式是否合法
		err := emailformat.Vaild()
		if err != nil {
			logx.Error(err)
			continue
		}

		// 加锁,避免并发修改 emailSending 字典
		m.lock.Lock()

		// 检查 emailSending 字典中是否已存在相同的任务
		_, isSending := m.emailSending[emailformat.UniqueKey]
		if isSending {
			m.lock.Unlock()
			continue
		}

		// 将邮件任务添加到 emailSending 字典中
		m.emailSending[emailformat.UniqueKey] = &EmailTask{
			Email:    emailformat,
			SendTime: time.Now().UTC(),
		}
		m.lock.Unlock()

		// 获取一个信号量,限制同时发送的邮件任务数量
		m.semaphore <- struct{}{}

		go func() {
			defer func() { <-m.semaphore }() // 释放一个信号量

			// 渲染邮件模板内容
			content := RenderEmailTemplate(
				emailformat.TemplateName,
				emailformat.CompanyName,
				emailformat.ConfirmationLink,
				emailformat.SenderName,
				emailformat.SenderTitle,
				emailformat.Extend,
			)

			// 发送邮件
			err := smtp.SendMail("smtp.gmail.com:587", m.Auth, m.FromEmail, []string{emailformat.TargetEmail}, content)
			if err != nil {
				log.Printf("Failed to send email to %s: %v\n", emailformat, err)
				// 重新发送邮件
				m.Resend(emailformat.UniqueKey, content)
			}
		}()
	}
}

// Resend 是 EmailSender 结构体的方法,用于重发邮件。
func (m *EmailSender) Resend(uniqueKey string, content []byte) {
	// 等待一段时间后重发邮件,避免频繁重发
	time.Sleep(m.ResendTimeLimit)

	m.lock.Lock()
	defer m.lock.Unlock()

	// 检查邮件任务是否仍存在并且未成功发送
	if task, ok := m.emailSending[uniqueKey]; ok && task.SendTime.Add(m.ResendTimeLimit).After(time.Now().UTC()) {
		err := smtp.SendMail(task.Email.TargetEmail, m.Auth, m.FromEmail, []string{task.Email.TargetEmail}, content)
		if err != nil {
			log.Printf("Failed to resend email to %s: %v\n", task.Email.TargetEmail, err)
		} else {
			// 从 emailSending 字典中删除已成功发送的邮件任务
			delete(m.emailSending, uniqueKey)
		}
	}
}

// ClearExpiredTasks 是 EmailSender 结构体的方法,用于清除过期的邮件任务。
func (m *EmailSender) ClearExpiredTasks() {
	// 每分钟触发一次清除操作
	ticker := time.NewTicker(time.Minute)
	defer ticker.Stop()

	for {
		<-ticker.C

		m.lock.Lock()
		// 遍历 emailSending 字典,删除发送时间超过一定限制的过期任务
		for email, task := range m.emailSending {
			if task.SendTime.Add(m.ResendTimeLimit).Before(time.Now().UTC()) {
				delete(m.emailSending, email)
			}
		}
		m.lock.Unlock()
	}
}

// RenderEmailTemplate 是一个渲染邮件模板的函数,根据给定的参数生成邮件内容。
// 参数:
//   - templateName: 邮件模板的名称
//   - companyName: 公司名称
//   - confirmationLink: 确认链接
//   - senderName: 发件人姓名
//   - senderTitle: 发件人职务
//   - extend: 扩展字段,包含其他自定义键值对的映射
// 返回值:
//   - []byte: 渲染后的邮件内容(以字节切片形式返回)

func RenderEmailTemplate(templateName, companyName, confirmationLink, senderName, senderTitle string, extend map[string]string) []byte {
	// 创建一个包含邮件模板所需数据的映射
	data := map[string]string{
		"CompanyName":      companyName,
		"ConfirmationLink": confirmationLink,
		"SenderName":       senderName,
		"SenderTitle":      senderTitle,
	}

	// 将扩展字段中的键值对添加到数据映射中
	for k, v := range extend {
		data[k] = v
	}

	// 创建一个字节缓冲区,用于存储渲染后的邮件内容
	var result bytes.Buffer

	// 使用给定的数据映射执行邮件模板渲染
	err := tpls.ExecuteTemplate(&result, templateName, data)
	if err != nil {
		log.Fatal(err)
	}

	// 将渲染后的邮件内容转换为字节切片并返回
	return result.Bytes()
}