package usecase

import (
	"fmt"

	libmongo "gateway/internal/library/mongo"
	redislib "gateway/internal/library/redis"
	domrepo "gateway/internal/model/auth/domain/repository"
	domusecase "gateway/internal/model/auth/domain/usecase"
	"gateway/internal/model/auth/repository"
)

// Module bundles auth atomic primitives.
type Module struct {
	Invite              domusecase.InviteUseCase
	RegistrationMeta    domusecase.RegistrationMetaUseCase
	RegistrationSession domusecase.RegistrationSessionUseCase
	LoginSession        domusecase.LoginSessionUseCase
	LoginMFAChallenge   domusecase.LoginMFAChallengeUseCase

	Invites              domrepo.InviteRepository
	RegistrationMetaRepo domrepo.RegistrationMetaRepository
}

// ModuleParam wires auth module dependencies.
type ModuleParam struct {
	Redis     *redislib.Client
	MongoConf *libmongo.Conf

	// Optional overrides for tests.
	Invites domrepo.InviteRepository
	Lock    domrepo.InviteConsumeLock
}

// NewModuleFromParam builds auth atomic usecases.
func NewModuleFromParam(param ModuleParam) (*Module, error) {
	if param.Redis == nil || param.Redis.Zero() == nil {
		return nil, fmt.Errorf("auth: redis is required")
	}
	if param.MongoConf == nil || param.MongoConf.Host == "" {
		return nil, fmt.Errorf("auth: mongo is required for invite usecase")
	}

	invites := param.Invites
	if invites == nil {
		invites = repository.NewInviteRepository(repository.InviteRepositoryParam{Conf: param.MongoConf})
	}
	regMetaRepo := repository.NewRegistrationMetaRepository(repository.RegistrationMetaRepositoryParam{Conf: param.MongoConf})
	sessionStore := repository.NewRedisRegistrationSessionStore(param.Redis)
	loginStore := repository.NewRedisLoginSessionStore(param.Redis)
	loginMFAStore := repository.NewRedisLoginMFAChallengeStore(param.Redis)
	lock := param.Lock
	if lock == nil {
		lock = repository.NewRedisInviteConsumeLock(param.Redis)
	}

	mod := &Module{
		Invites:              invites,
		RegistrationMetaRepo: regMetaRepo,
		Invite: MustInviteUseCase(InviteUseCaseParam{
			Repo: invites,
			Lock: lock,
		}),
		RegistrationMeta: MustRegistrationMetaUseCase(RegistrationMetaUseCaseParam{
			Repo: regMetaRepo,
		}),
		RegistrationSession: MustRegistrationSessionUseCase(RegistrationSessionUseCaseParam{
			Store: sessionStore,
		}),
		LoginSession: MustLoginSessionUseCase(LoginSessionUseCaseParam{
			Store: loginStore,
		}),
		LoginMFAChallenge: MustLoginMFAChallengeUseCase(LoginMFAChallengeUseCaseParam{
			Store: loginMFAStore,
		}),
	}
	return mod, nil
}
