// Code scaffolded by goctl. Safe to edit.
// goctl 1.10.1

package svc

import (
	"context"

	"gateway/internal/config"
	libmongo "gateway/internal/library/mongo"
	redislib "gateway/internal/library/redis"
	"gateway/internal/library/validate"
	"gateway/internal/library/zitadel"
	"gateway/internal/middleware"
	authdomrepo "gateway/internal/model/auth/domain/repository"
	domauth "gateway/internal/model/auth/domain/usecase"
	authrepo "gateway/internal/model/auth/repository"
	authusecase "gateway/internal/model/auth/usecase"
	domrepo "gateway/internal/model/member/domain/repository"
	dommember "gateway/internal/model/member/domain/usecase"
	memberusecase "gateway/internal/model/member/usecase"
	domnotif "gateway/internal/model/notification/domain/usecase"
	notifusecase "gateway/internal/model/notification/usecase"
	dompermrepo "gateway/internal/model/permission/domain/repository"
	domperm "gateway/internal/model/permission/domain/usecase"
	permusecase "gateway/internal/model/permission/usecase"
	"gateway/internal/worker/notification_retry"

	"github.com/zeromicro/go-zero/rest"
)

type ServiceContext struct {
	Config                  config.Config
	Validator               validate.Validate
	Redis                   *redislib.Client
	AuthToken               domauth.TokenUseCase
	AuthInvite              domauth.InviteUseCase
	AuthRegistrationMeta    domauth.RegistrationMetaUseCase
	AuthRegistrationSession domauth.RegistrationSessionUseCase
	AuthLoginSession        domauth.LoginSessionUseCase
	AuthLoginMFAChallenge   domauth.LoginMFAChallengeUseCase
	Zitadel                 *zitadel.Client
	Notifier                domnotif.NotifierUseCase
	NotificationAdmin       domnotif.AdminNotifierUseCase
	NotificationRetry       *notification_retry.Runner

	MemberOTP          dommember.OTPUseCase
	MemberTOTP         dommember.TOTPUseCase
	MemberStepUp       dommember.StepUpUseCase
	MemberProfile      dommember.ProfileUseCase
	MemberLifecycle    dommember.LifecycleUseCase
	MemberProvisioning dommember.ProvisioningUseCase
	MemberTenant       dommember.TenantUseCase
	MemberVerifyRate   dommember.VerifyRateUseCase
	MemberRepo         domrepo.MemberRepository

	PermissionCatalog        domperm.PermissionUseCase
	PermissionRole           domperm.RoleUseCase
	PermissionRolePermission domperm.RolePermissionUseCase
	PermissionUserRole       domperm.UserRoleUseCase
	PermissionRoleMapping    domperm.RoleMappingUseCase
	PermissionAuthQuery      domperm.AuthorizationQueryUseCase
	PermissionRBAC           domperm.RBACUseCase
	PermissionRoleRepo       dompermrepo.RoleRepository
	permissionModule         *permusecase.Module

	// Middlewares mounted per route group via .api `middleware:` directive.
	// AuthJWT enforces Bearer token (sets actor on ctx).
	// CasbinRBAC enforces RBAC; must be chained AFTER AuthJWT.
	AuthJWT    rest.Middleware
	CasbinRBAC rest.Middleware
}

func NewServiceContext(c config.Config) *ServiceContext {
	v, err := validate.NewWithDefaultEN()
	if err != nil {
		panic(err)
	}

	rds, err := redislib.NewClient(c.Redis)
	if err != nil {
		panic(err)
	}

	sc := &ServiceContext{
		Config:    c,
		Validator: v,
		Redis:     rds,
	}
	authCfg := c.Auth.Defaults()
	if authCfg.Enabled() {
		var revoke authdomrepo.TokenRevokeStore
		if rds != nil && rds.Zero() != nil {
			revoke = authrepo.NewRedisTokenRevokeStore(rds)
		}
		sc.AuthToken = authusecase.MustTokenUseCase(authusecase.TokenUseCaseParam{
			Config: authCfg,
			Revoke: revoke,
		})
	}
	zClient, err := zitadel.NewClient(c.Zitadel)
	if err != nil {
		panic(err)
	}
	sc.Zitadel = zClient
	if c.Mongo.Host != "" {
		mod, err := notifusecase.NewModuleFromParam(notifusecase.FactoryParam{
			MongoConf: &c.Mongo,
			Redis:     rds,
			Config:    c.Notification,
		})
		if err != nil {
			panic(err)
		}
		sc.Notifier = mod.Notifier
		sc.NotificationAdmin = mod.Admin
		sc.NotificationRetry = notification_retry.NewRunner(mod.RetryWorker)

		authMod, err := authusecase.NewModuleFromParam(authusecase.ModuleParam{
			MongoConf: &c.Mongo,
			Redis:     rds,
		})
		if err != nil {
			panic(err)
		}
		sc.AuthInvite = authMod.Invite
		sc.AuthRegistrationMeta = authMod.RegistrationMeta
		sc.AuthRegistrationSession = authMod.RegistrationSession
		sc.AuthLoginSession = authMod.LoginSession
		sc.AuthLoginMFAChallenge = authMod.LoginMFAChallenge
	}
	if rds != nil && rds.Zero() != nil {
		var mongoConf *libmongo.Conf
		if c.Mongo.Host != "" {
			mongoConf = &c.Mongo
		}
		memberMod, err := memberusecase.NewModuleFromParam(memberusecase.ModuleParam{
			Redis:     rds,
			MongoConf: mongoConf,
			Config:    c.Member,
		})
		if err != nil {
			panic(err)
		}
		sc.MemberOTP = memberMod.OTP
		sc.MemberTOTP = memberMod.TOTP
		sc.MemberStepUp = memberMod.StepUp
		sc.MemberProfile = memberMod.Profile
		sc.MemberLifecycle = memberMod.Lifecycle
		sc.MemberProvisioning = memberMod.Provisioning
		sc.MemberTenant = memberMod.Tenant
		sc.MemberVerifyRate = memberMod.VerifyRate
		sc.MemberRepo = memberMod.Members
	}
	if c.Mongo.Host != "" {
		permMod, err := permusecase.NewModuleFromParam(permusecase.FactoryParam{
			MongoConf: &c.Mongo,
			Redis:     rds,
			Config:    c.Permission,
		})
		if err != nil {
			panic(err)
		}
		sc.PermissionCatalog = permMod.Permission
		sc.PermissionRole = permMod.Role
		sc.PermissionRolePermission = permMod.RolePermission
		sc.PermissionUserRole = permMod.UserRole
		sc.PermissionRoleMapping = permMod.RoleMapping
		sc.PermissionAuthQuery = permMod.AuthorizationQuery
		sc.PermissionRBAC = permMod.RBAC
		sc.PermissionRoleRepo = permMod.Roles
		sc.permissionModule = permMod
	}

	// Wire middleware last so dependencies (AuthToken / PermissionRBAC)
	// are populated. Each middleware tolerates nil deps (auth → 501,
	// rbac → passthrough) so the gateway boots even with partial config.
	sc.AuthJWT = middleware.NewAuthJWTMiddleware(sc.AuthToken).Handle
	sc.CasbinRBAC = middleware.NewCasbinRBACMiddleware(sc.PermissionRBAC, middleware.CasbinRBACOptions{}).Handle

	return sc
}

func (sc *ServiceContext) StartWorkers(ctx context.Context) {
	if sc.NotificationRetry != nil {
		sc.NotificationRetry.Start(ctx)
	}
	if sc.permissionModule != nil {
		if err := sc.permissionModule.StartBackground(ctx); err != nil {
			panic(err)
		}
	}
}

func (sc *ServiceContext) StopWorkers() {
	if sc.NotificationRetry != nil {
		sc.NotificationRetry.Stop()
	}
	if sc.permissionModule != nil {
		sc.permissionModule.StopBackground()
	}
}
