package usecase

import (
	"backend/pkg/notification/config"
	"backend/pkg/notification/domain"
	"backend/pkg/notification/domain/entity"
	"backend/pkg/notification/domain/repository"
	"backend/pkg/notification/domain/usecase"
	"context"
	"fmt"
	"math"
	"sort"
	"time"

	"backend/pkg/library/errs"
	"backend/pkg/library/errs/code"

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

// DeliveryUseCaseParam 傳送參數配置
type DeliveryUseCaseParam struct {
	SMSProviders   []usecase.SMSProvider
	EmailProviders []usecase.EmailProvider
	DeliveryConfig config.DeliveryConfig
	HistoryRepo    repository.HistoryRepository // 可選的歷史記錄 repository
}

// DeliveryUseCase 通知發送服務
type DeliveryUseCase struct {
	param DeliveryUseCaseParam
}

func MustDeliveryUseCase(param DeliveryUseCaseParam) usecase.DeliveryUseCase {
	// 設置默認配置
	if param.DeliveryConfig.MaxRetries == 0 {
		param.DeliveryConfig.MaxRetries = 3
	}
	if param.DeliveryConfig.InitialDelay == 0 {
		param.DeliveryConfig.InitialDelay = 100 * time.Millisecond
	}
	if param.DeliveryConfig.BackoffFactor == 0 {
		param.DeliveryConfig.BackoffFactor = 2.0
	}
	if param.DeliveryConfig.MaxDelay == 0 {
		param.DeliveryConfig.MaxDelay = 30 * time.Second
	}
	if param.DeliveryConfig.Timeout == 0 {
		param.DeliveryConfig.Timeout = 30 * time.Second
	}

	return &DeliveryUseCase{
		param: param,
	}
}

func (use *DeliveryUseCase) SendMessage(ctx context.Context, req usecase.SMSMessageRequest) error {
	// 創建歷史記錄
	history := &entity.DeliveryHistory{
		Type:         "sms",
		Recipient:    req.PhoneNumber,
		Subject:      "",
		Content:      req.MessageContent,
		Status:       entity.DeliveryStatusPending,
		AttemptCount: 0,
		CreatedAt:    time.Now(),
		UpdatedAt:    time.Now(),
	}

	if use.param.DeliveryConfig.EnableHistory && use.param.HistoryRepo != nil {
		if err := use.param.HistoryRepo.CreateHistory(ctx, history); err != nil {
			logx.WithContext(ctx).Errorf("Failed to create SMS history: %v", err)
		}
	}

	// 執行發送邏輯
	return use.sendSMSWithRetry(ctx, req, history)
}

func (use *DeliveryUseCase) SendEmail(ctx context.Context, req usecase.MailReq) error {
	// 創建歷史記錄
	history := &entity.DeliveryHistory{
		Type:         "email",
		Recipient:    fmt.Sprintf("%v", req.To),
		Subject:      req.Subject,
		Content:      req.Body,
		Status:       entity.DeliveryStatusPending,
		AttemptCount: 0,
		CreatedAt:    time.Now(),
		UpdatedAt:    time.Now(),
	}

	if use.param.DeliveryConfig.EnableHistory && use.param.HistoryRepo != nil {
		if err := use.param.HistoryRepo.CreateHistory(ctx, history); err != nil {
			logx.WithContext(ctx).Errorf("Failed to create email history: %v", err)
		}
	}

	// 執行發送邏輯
	return use.sendEmailWithRetry(ctx, req, history)
}

// sendSMSWithRetry 發送 SMS 並實現重試機制
func (use *DeliveryUseCase) sendSMSWithRetry(ctx context.Context, req usecase.SMSMessageRequest, history *entity.DeliveryHistory) error {
	// 根據 Sort 欄位對 SMSProviders 進行排序
	providers := make([]usecase.SMSProvider, len(use.param.SMSProviders))
	copy(providers, use.param.SMSProviders)
	sort.Slice(providers, func(i, j int) bool {
		return providers[i].Sort < providers[j].Sort
	})

	var lastErr error
	totalAttempts := 0

	// 嘗試所有 providers
	for providerIndex, provider := range providers {
		// 為每個 provider 嘗試發送
		for attempt := 0; attempt < use.param.DeliveryConfig.MaxRetries; attempt++ {
			totalAttempts++

			// 更新歷史記錄狀態
			history.Status = entity.DeliveryStatusSending
			history.Provider = fmt.Sprintf("sms_provider_%d", providerIndex)
			history.AttemptCount = totalAttempts
			history.UpdatedAt = time.Now()
			use.updateHistory(ctx, history)

			// 記錄發送嘗試
			attemptStart := time.Now()

			// 創建帶超時的 context
			sendCtx, cancel := context.WithTimeout(ctx, use.param.DeliveryConfig.Timeout)

			err := provider.Repo.SendSMS(sendCtx, repository.SMSMessageRequest{
				PhoneNumber:    req.PhoneNumber,
				RecipientName:  req.RecipientName,
				MessageContent: req.MessageContent,
			})

			cancel()

			// 記錄嘗試結果
			attemptDuration := time.Since(attemptStart)
			attemptRecord := entity.DeliveryAttempt{
				Provider:     history.Provider,
				AttemptAt:    attemptStart,
				Success:      err == nil,
				ErrorMessage: "",
				Duration:     attemptDuration.Milliseconds(),
			}

			if err != nil {
				attemptRecord.ErrorMessage = err.Error()
				lastErr = err

				logx.WithContext(ctx).Errorf("SMS send attempt %d failed for provider %d: %v",
					attempt+1, providerIndex, err)

				// 如果不是最後一次嘗試，等待後重試
				if attempt < use.param.DeliveryConfig.MaxRetries-1 {
					delay := use.calculateDelay(attempt)
					history.Status = entity.DeliveryStatusRetrying
					use.updateHistory(ctx, history)
					use.addAttemptRecord(ctx, history.ID, attemptRecord)

					select {
					case <-ctx.Done():
						return ctx.Err()
					case <-time.After(delay):
						continue
					}
				}
			} else {
				// 發送成功
				history.Status = entity.DeliveryStatusSuccess
				history.UpdatedAt = time.Now()
				now := time.Now()
				history.CompletedAt = &now
				use.updateHistory(ctx, history)
				use.addAttemptRecord(ctx, history.ID, attemptRecord)

				logx.WithContext(ctx).Infof("SMS sent successfully after %d attempts", totalAttempts)
				return nil
			}

			use.addAttemptRecord(ctx, history.ID, attemptRecord)
		}
	}

	// 所有 providers 都失敗了
	history.Status = entity.DeliveryStatusFailed
	history.ErrorMessage = fmt.Sprintf("All providers failed. Last error: %v", lastErr)
	history.UpdatedAt = time.Now()
	now := time.Now()
	history.CompletedAt = &now
	use.updateHistory(ctx, history)

	return errs.ThirdPartyError(
		code.CloudEPNotification,
		domain.FailedToSendSMSErrorCode,
		fmt.Sprintf("Failed to send SMS after %d attempts across %d providers",
			totalAttempts, len(providers)))
}

// sendEmailWithRetry 發送 Email 並實現重試機制
func (use *DeliveryUseCase) sendEmailWithRetry(ctx context.Context, req usecase.MailReq, history *entity.DeliveryHistory) error {
	// 根據 Sort 欄位對 EmailProviders 進行排序
	providers := make([]usecase.EmailProvider, len(use.param.EmailProviders))
	copy(providers, use.param.EmailProviders)
	sort.Slice(providers, func(i, j int) bool {
		return providers[i].Sort < providers[j].Sort
	})

	var lastErr error
	totalAttempts := 0

	// 嘗試所有 providers
	for providerIndex, provider := range providers {
		// 為每個 provider 嘗試發送
		for attempt := 0; attempt < use.param.DeliveryConfig.MaxRetries; attempt++ {
			totalAttempts++

			// 更新歷史記錄狀態
			history.Status = entity.DeliveryStatusSending
			history.Provider = fmt.Sprintf("email_provider_%d", providerIndex)
			history.AttemptCount = totalAttempts
			history.UpdatedAt = time.Now()
			use.updateHistory(ctx, history)

			// 記錄發送嘗試
			attemptStart := time.Now()

			// 創建帶超時的 context
			sendCtx, cancel := context.WithTimeout(ctx, use.param.DeliveryConfig.Timeout)

			err := provider.Repo.SendMail(sendCtx, repository.MailReq{
				From:    req.From,
				To:      req.To,
				Subject: req.Subject,
				Body:    req.Body,
			})

			cancel()

			// 記錄嘗試結果
			attemptDuration := time.Since(attemptStart)
			attemptRecord := entity.DeliveryAttempt{
				Provider:     history.Provider,
				AttemptAt:    attemptStart,
				Success:      err == nil,
				ErrorMessage: "",
				Duration:     attemptDuration.Milliseconds(),
			}

			if err != nil {
				attemptRecord.ErrorMessage = err.Error()
				lastErr = err

				logx.WithContext(ctx).Errorf("Email send attempt %d failed for provider %d: %v",
					attempt+1, providerIndex, err)

				// 如果不是最後一次嘗試，等待後重試
				if attempt < use.param.DeliveryConfig.MaxRetries-1 {
					delay := use.calculateDelay(attempt)
					history.Status = entity.DeliveryStatusRetrying
					use.updateHistory(ctx, history)
					use.addAttemptRecord(ctx, history.ID, attemptRecord)

					select {
					case <-ctx.Done():
						return ctx.Err()
					case <-time.After(delay):
						continue
					}
				}
			} else {
				// 發送成功
				history.Status = entity.DeliveryStatusSuccess
				history.UpdatedAt = time.Now()
				now := time.Now()
				history.CompletedAt = &now
				use.updateHistory(ctx, history)
				use.addAttemptRecord(ctx, history.ID, attemptRecord)

				logx.WithContext(ctx).Infof("Email sent successfully after %d attempts", totalAttempts)
				return nil
			}

			use.addAttemptRecord(ctx, history.ID, attemptRecord)
		}
	}

	// 所有 providers 都失敗了
	history.Status = entity.DeliveryStatusFailed
	history.ErrorMessage = fmt.Sprintf("All providers failed. Last error: %v", lastErr)
	history.UpdatedAt = time.Now()
	now := time.Now()
	history.CompletedAt = &now
	use.updateHistory(ctx, history)

	return errs.ThirdPartyError(
		code.CloudEPNotification,
		domain.FailedToSendEmailErrorCode,
		fmt.Sprintf("Failed to send email after %d attempts across %d providers",
			totalAttempts, len(providers)))
}

// calculateDelay 計算指數退避延遲
func (use *DeliveryUseCase) calculateDelay(attempt int) time.Duration {
	delay := float64(use.param.DeliveryConfig.InitialDelay) * math.Pow(use.param.DeliveryConfig.BackoffFactor, float64(attempt))

	if delay > float64(use.param.DeliveryConfig.MaxDelay) {
		delay = float64(use.param.DeliveryConfig.MaxDelay)
	}

	return time.Duration(delay)
}

// updateHistory 更新歷史記錄
func (use *DeliveryUseCase) updateHistory(ctx context.Context, history *entity.DeliveryHistory) {
	if use.param.DeliveryConfig.EnableHistory && use.param.HistoryRepo != nil {
		if err := use.param.HistoryRepo.UpdateHistory(ctx, history); err != nil {
			logx.WithContext(ctx).Errorf("Failed to update delivery history: %v", err)
		}
	}
}

// addAttemptRecord 添加發送嘗試記錄
func (use *DeliveryUseCase) addAttemptRecord(ctx context.Context, historyID string, attempt entity.DeliveryAttempt) {
	if use.param.DeliveryConfig.EnableHistory && use.param.HistoryRepo != nil {
		if err := use.param.HistoryRepo.AddAttempt(ctx, historyID, attempt); err != nil {
			logx.WithContext(ctx).Errorf("Failed to add attempt record: %v", err)
		}
	}
}
