package logic

import (
	"bytes"
	"log"
	"net/smtp"
	"sync"
	"text/template"
	"time"
)

var EmailManager *EmailSender

type EmailFormat struct {
	TargetEmail      string // 发送的目标email
	CompanyName      string // fs公司名
	ConfirmationLink string // fs确认连接
	SenderName       string // 发送人
	SenderTitle      string // 发送标题
}

// 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    // 处理的任务时间
}

func (m *EmailSender) ProcessEmailTasks() {
	for {
		emailformat, ok := <-m.EmailTasks
		if !ok {
			log.Println("Email task channel closed")
			break
		}

		m.lock.Lock()
		_, isSending := m.emailSending[emailformat.TargetEmail]
		if isSending {
			m.lock.Unlock()
			continue
		}

		m.emailSending[emailformat.TargetEmail] = &EmailTask{
			Email:    emailformat,
			SendTime: time.Now(),
		}
		m.lock.Unlock()

		// Acquire a token
		m.semaphore <- struct{}{}

		go func() {
			defer func() { <-m.semaphore }() // Release a token

			content := RenderEmailTemplate(emailformat.CompanyName, emailformat.ConfirmationLink, emailformat.SenderName, emailformat.SenderTitle)
			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.TargetEmail, content)
			}
		}()
	}
}

// Resend 重发邮件
func (m *EmailSender) Resend(emailTarget string, content []byte) {
	time.Sleep(m.ResendTimeLimit)

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

	// Check if the email task still exists and has not been sent successfully
	if task, ok := m.emailSending[emailTarget]; ok && task.SendTime.Add(m.ResendTimeLimit).After(time.Now()) {
		err := smtp.SendMail(emailTarget, m.Auth, m.FromEmail, []string{emailTarget}, content)
		if err != nil {
			log.Printf("Failed to resend email to %s: %v\n", emailTarget, err)
		} else {
			delete(m.emailSending, emailTarget)
		}
	}
}

// ClearExpiredTasks 清除过期的邮件任务
func (m *EmailSender) ClearExpiredTasks() {
	ticker := time.NewTicker(time.Minute)
	defer ticker.Stop()

	for {
		<-ticker.C

		m.lock.Lock()
		for email, task := range m.emailSending {
			if task.SendTime.Add(m.ResendTimeLimit).Before(time.Now()) {
				delete(m.emailSending, email)
			}
		}
		m.lock.Unlock()
	}
}

func init() {

	// Initialize the email manager
	EmailManager = &EmailSender{
		EmailTasks: make(chan *EmailFormat, 10),
		Auth: smtp.PlainAuth(
			"",
			"user@example.com",
			"password",
			"smtp.gmail.com",
		),
		FromEmail:       "user@example.com",
		emailSending:    make(map[string]*EmailTask, 10),
		ResendTimeLimit: time.Minute * 1,
		semaphore:       make(chan struct{}, 10), // Initialize semaphore with a capacity of 10
	}

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

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

const emailTemplate = `Subject: Your {{.CompanyName}} Account Confirmation

Dear

Thank you for creating an account with {{.CompanyName}}. We're excited to have you on board!

Before we get started, we just need to confirm that this is the right email address. Please confirm your email address by clicking on the link below:

{{.ConfirmationLink}}

Once you've confirmed, you can get started with {{.CompanyName}}. If you have any questions, feel free to reply to this email. We're here to help!

If you did not create an account with us, please ignore this email.

Thanks,
{{.SenderName}}
{{.SenderTitle}}
{{.CompanyName}}
`

func RenderEmailTemplate(companyName, confirmationLink, senderName, senderTitle string) []byte {
	tmpl, err := template.New("email").Parse(emailTemplate)
	if err != nil {
		log.Fatal(err)
	}

	data := map[string]string{
		"CompanyName":      companyName,
		"ConfirmationLink": confirmationLink,
		"SenderName":       senderName,
		"SenderTitle":      senderTitle,
	}

	var result bytes.Buffer
	err = tmpl.Execute(&result, data)
	if err != nil {
		log.Fatal(err)
	}

	return result.Bytes()
}