import { Inject, Injectable, InternalServerErrorException } from '@nestjs/common'; // Core Use Cases import { LoginUseCase } from '@core/identity/application/use-cases/LoginUseCase'; import { LogoutUseCase } from '@core/identity/application/use-cases/LogoutUseCase'; import { SignupUseCase } from '@core/identity/application/use-cases/SignupUseCase'; // Core Interfaces and Tokens import { AuthenticatedUserDTO as CoreAuthenticatedUserDTO } from '@core/identity/application/dto/AuthenticatedUserDTO'; import { IdentitySessionPort } from '@core/identity/application/ports/IdentitySessionPort'; import { User } from '@core/identity/domain/entities/User'; import type { IAuthRepository } from '@core/identity/domain/repositories/IAuthRepository'; import type { IUserRepository } from '@core/identity/domain/repositories/IUserRepository'; import type { IPasswordHashingService } from '@core/identity/domain/services/PasswordHashingService'; import type { Logger } from "@core/shared/application"; import { AUTH_REPOSITORY_TOKEN, IDENTITY_SESSION_PORT_TOKEN, LOGGER_TOKEN, PASSWORD_HASHING_SERVICE_TOKEN, USER_REPOSITORY_TOKEN } from './AuthProviders'; import { AuthSessionDTO, LoginParams, SignupParams, AuthenticatedUserDTO } from './dtos/AuthDto'; import { AuthSessionPresenter } from './presenters/AuthSessionPresenter'; import { CommandResultPresenter } from './presenters/CommandResultPresenter'; @Injectable() export class AuthService { private readonly loginUseCase: LoginUseCase; private readonly signupUseCase: SignupUseCase; private readonly logoutUseCase: LogoutUseCase; constructor( @Inject(AUTH_REPOSITORY_TOKEN) private authRepository: IAuthRepository, @Inject(PASSWORD_HASHING_SERVICE_TOKEN) private passwordHashingService: IPasswordHashingService, @Inject(LOGGER_TOKEN) private logger: Logger, @Inject(IDENTITY_SESSION_PORT_TOKEN) private identitySessionPort: IdentitySessionPort, @Inject(USER_REPOSITORY_TOKEN) private userRepository: IUserRepository, // Inject IUserRepository here ) { this.loginUseCase = new LoginUseCase(this.authRepository, this.passwordHashingService); this.signupUseCase = new SignupUseCase(this.authRepository, this.passwordHashingService); this.logoutUseCase = new LogoutUseCase(this.identitySessionPort); } private mapUserToAuthenticatedUserDTO(user: User): AuthenticatedUserDTO { return { userId: user.getId().value, email: user.getEmail() ?? '', displayName: user.getDisplayName() ?? '', }; } private mapToCoreAuthenticatedUserDTO(apiDto: AuthenticatedUserDTO): CoreAuthenticatedUserDTO { return { id: apiDto.userId, displayName: apiDto.displayName, email: apiDto.email, }; } async getCurrentSession(): Promise { this.logger.debug('[AuthService] Attempting to get current session.'); const coreSession = await this.identitySessionPort.getCurrentSession(); if (!coreSession) { return null; } const user = await this.userRepository.findById(coreSession.user.id); // Use userRepository to fetch full user if (!user) { // If session exists but user doesn't in DB, perhaps clear session? this.logger.warn(`[AuthService] Session found for user ID ${coreSession.user.id}, but user not found in repository.`); await this.identitySessionPort.clearSession(); // Clear potentially stale session return null; } const authenticatedUserDTO = this.mapUserToAuthenticatedUserDTO(User.fromStored(user)); const presenter = new AuthSessionPresenter(); presenter.present({ token: coreSession.token, user: authenticatedUserDTO }); return presenter; } async signupWithEmail(params: SignupParams): Promise { this.logger.debug(`[AuthService] Attempting signup for email: ${params.email}`); const user = await this.signupUseCase.execute(params.email, params.password, params.displayName); // Create session after successful signup const authenticatedUserDTO = this.mapUserToAuthenticatedUserDTO(user); const coreDto = this.mapToCoreAuthenticatedUserDTO(authenticatedUserDTO); const session = await this.identitySessionPort.createSession(coreDto); const presenter = new AuthSessionPresenter(); presenter.present({ token: session.token, user: authenticatedUserDTO }); return presenter; } async loginWithEmail(params: LoginParams): Promise { this.logger.debug(`[AuthService] Attempting login for email: ${params.email}`); try { const user = await this.loginUseCase.execute(params.email, params.password); // Create session after successful login const authenticatedUserDTO = this.mapUserToAuthenticatedUserDTO(user); const coreDto = this.mapToCoreAuthenticatedUserDTO(authenticatedUserDTO); const session = await this.identitySessionPort.createSession(coreDto); const presenter = new AuthSessionPresenter(); presenter.present({ token: session.token, user: authenticatedUserDTO }); return presenter; } catch (error) { this.logger.error(`[AuthService] Login failed for email ${params.email}:`, error instanceof Error ? error : new Error(String(error))); throw new InternalServerErrorException('Login failed due to invalid credentials or server error.'); } } async logout(): Promise { this.logger.debug('[AuthService] Attempting logout.'); const presenter = new CommandResultPresenter(); await this.logoutUseCase.execute(); presenter.present({ success: true }); return presenter; } }