package usecase

import (
	"fmt"
	"time"

	"gateway/internal/library/mongo"
	redislib "gateway/internal/library/redis"
	notifconfig "gateway/internal/model/notification/config"
	domtpl "gateway/internal/model/notification/domain/template"
	domusecase "gateway/internal/model/notification/domain/usecase"
	"gateway/internal/model/notification/provider/email"
	"gateway/internal/model/notification/provider/sms"
)

// FactoryParam wires notification module dependencies for production or tests.
type FactoryParam struct {
	MongoConf *mongo.Conf
	Redis     *redislib.Client // process-wide client from svc; nil → memory idempotency/quota, no async worker
	Config    notifconfig.Config
}

// NewNotifierUseCaseFromParam builds NotifierUseCase when Mongo is configured.
func NewNotifierUseCaseFromParam(param FactoryParam) (domusecase.NotifierUseCase, error) {
	mod, err := NewModuleFromParam(param)
	if err != nil {
		return nil, err
	}
	return mod.Notifier, nil
}

func buildEmailChain(cfg notifconfig.Config, rc *redislib.Client) (*email.Chain, error) {
	senders, err := collectEmailSenders(cfg)
	if err != nil {
		return nil, err
	}
	if len(senders) == 0 {
		switch cfg.Email.Provider {
		case "", notifconfig.ProviderMock:
			opts := []email.MockSenderOption{email.WithMockName(notifconfig.ProviderMock)}
			if r := rc.Zero(); r != nil {
				opts = append(opts, email.WithMockRedis(r, 0))
			}
			return email.NewChain(email.NewMockSender(opts...)), nil
		default:
			return nil, fmt.Errorf("notification: no email senders enabled and provider %q is not mock", cfg.Email.Provider)
		}
	}

	opts := []email.ChainOption{}
	if cfg.Email.SES.Enable {
		opts = append(opts, email.WithTimeout(90*time.Second))
	}
	if len(opts) == 0 {
		return email.NewChain(senders...), nil
	}
	return email.NewChainWithOptions(senders, opts...), nil
}

func collectEmailSenders(cfg notifconfig.Config) ([]email.Sender, error) {
	var senders []email.Sender

	if cfg.Email.SMTP.Enable {
		s, err := email.NewSMTPSender(email.SMTPSettings{
			Sort:     cfg.Email.SMTP.Sort,
			Host:     cfg.Email.SMTP.Host,
			Port:     cfg.Email.SMTP.Port,
			Username: cfg.Email.SMTP.Username,
			Password: cfg.Email.SMTP.Password,
		})
		if err != nil {
			return nil, fmt.Errorf("notification: smtp: %w", err)
		}
		senders = append(senders, s)
	}

	if cfg.Email.SES.Enable {
		s, err := email.NewSESSender(email.SESSettings{
			Sort:         cfg.Email.SES.Sort,
			Region:       cfg.Email.SES.Region,
			AccessKey:    cfg.Email.SES.AccessKey,
			SecretKey:    cfg.Email.SES.SecretKey,
			SessionToken: cfg.Email.SES.SessionToken,
		})
		if err != nil {
			return nil, fmt.Errorf("notification: ses: %w", err)
		}
		senders = append(senders, s)
	}

	return senders, nil
}

func buildSMSChain(cfg notifconfig.Config, rc *redislib.Client) (*sms.Chain, error) {
	senders, err := collectSMSSenders(cfg)
	if err != nil {
		return nil, err
	}
	if len(senders) == 0 {
		switch cfg.SMS.Provider {
		case "", notifconfig.ProviderMock:
			opts := []sms.MockSenderOption{sms.WithMockName(notifconfig.ProviderMock)}
			if r := rc.Zero(); r != nil {
				opts = append(opts, sms.WithMockRedis(r, 0))
			}
			return sms.NewChain(sms.NewMockSender(opts...)), nil
		default:
			return nil, fmt.Errorf("notification: no sms senders enabled and provider %q is not mock", cfg.SMS.Provider)
		}
	}
	return sms.NewChain(senders...), nil
}

func collectSMSSenders(cfg notifconfig.Config) ([]sms.Sender, error) {
	var senders []sms.Sender

	if cfg.SMS.Mitake.Enable {
		s, err := sms.NewMitakeSender(sms.MitakeSettings{
			Sort:     cfg.SMS.Mitake.Sort,
			User:     cfg.SMS.Mitake.User,
			Password: cfg.SMS.Mitake.Password,
		}, nil)
		if err != nil {
			return nil, fmt.Errorf("notification: mitake: %w", err)
		}
		senders = append(senders, s)
	}

	return senders, nil
}

func fallbackLocales(defaultLocale string) []string {
	if defaultLocale == "" {
		return []string{domtpl.LocaleZhTW, domtpl.LocaleEnUS}
	}
	if defaultLocale == domtpl.LocaleZhTW {
		return []string{domtpl.LocaleZhTW, domtpl.LocaleEnUS}
	}
	return []string{defaultLocale, domtpl.LocaleZhTW, domtpl.LocaleEnUS}
}
