package usecase

import (
	"fmt"

	"gateway/internal/model/notification"
	notifconfig "gateway/internal/model/notification/config"
	domrepo "gateway/internal/model/notification/domain/repository"
	domusecase "gateway/internal/model/notification/domain/usecase"
	"gateway/internal/model/notification/repository"
	"gateway/internal/model/notification/template"
)

// Module bundles notification use cases and background workers.
type Module struct {
	Notifier    domusecase.NotifierUseCase
	Admin       domusecase.AdminNotifierUseCase
	RetryWorker *RetryWorker
}

func retryQueueKey(cfg notifconfig.Config) string {
	if cfg.Async.QueueRedisKey != "" {
		return cfg.Async.QueueRedisKey
	}
	return notification.GetRetryZSetRedisKey()
}

// NewModuleFromParam wires notification repository, notifier, and optional retry worker.
func NewModuleFromParam(param FactoryParam) (*Module, error) {
	if param.MongoConf == nil || param.MongoConf.Host == "" {
		return nil, fmt.Errorf("notification: mongo config is required")
	}

	repo := repository.NewNotificationRepository(repository.NotificationRepositoryParam{
		Conf: param.MongoConf,
	})
	dlqRepo := repository.NewNotificationDLQRepository(repository.NotificationDLQRepositoryParam{
		Conf: param.MongoConf,
	})

	var idem = repository.NewMemoryIdempotencyCache()
	var quota = repository.NewMemoryQuotaCounter()
	var queue domrepo.RetryQueue
	if param.Redis != nil {
		idem = repository.NewRedisIdempotencyCache(param.Redis)
		quota = repository.NewRedisQuotaCounter(param.Redis)
		queue = repository.NewRedisRetryQueue(param.Redis, retryQueueKey(param.Config))
	}

	emailChain, err := buildEmailChain(param.Config, param.Redis)
	if err != nil {
		return nil, err
	}
	smsChain, err := buildSMSChain(param.Config, param.Redis)
	if err != nil {
		return nil, err
	}

	renderer := template.NewRenderer(
		template.DefaultRegistry(),
		fallbackLocales(param.Config.DefaultLocale)...,
	)

	notifier := MustNotifierUseCase(NotifierUseCaseParam{
		Repo:        repo,
		Idempotency: idem,
		Quota:       quota,
		RetryQueue:  queue,
		Renderer:    renderer,
		Email:       emailChain,
		SMS:         smsChain,
		Config:      param.Config,
	})

	mod := &Module{
		Notifier: notifier,
		Admin: NewAdminNotifierUseCase(AdminNotifierUseCaseParam{
			Repo:  repo,
			DLQ:   dlqRepo,
			Queue: queue,
		}),
	}
	if queue != nil {
		mod.RetryWorker = NewRetryWorker(RetryWorkerParam{
			Repo:     repo,
			DLQ:      dlqRepo,
			Queue:    queue,
			Renderer: renderer,
			Email:    emailChain,
			SMS:      smsChain,
			Config:   param.Config,
		})
	}
	return mod, nil
}
