import type { IUserRepository } from '../../../domain/ports/user-repository'
import type { IRefreshTokenRepository } from '../../../domain/ports/refresh-token-repository'
import type { ITokenService } from '../../../domain/ports/token-service'
import { AppError } from '../../../domain/errors/app-error'

export interface RefreshTokenStaffOutput {
  accessToken: string
  refreshToken: string
}

export class RefreshTokenStaff {
  constructor(
    private readonly userRepo: IUserRepository,
    private readonly tokenService: ITokenService,
    private readonly refreshTokenRepo: IRefreshTokenRepository,
  ) {}

  async execute(rawToken: string): Promise<RefreshTokenStaffOutput> {
    const tokenHash = this.tokenService.hashToken(rawToken)
    const record = await this.refreshTokenRepo.findValidByHash(tokenHash)

    if (!record) {
      throw new AppError('INVALID_TOKEN', 401, 'Refresh token inválido o expirado')
    }

    const user = await this.userRepo.findById(record.userId)
    if (!user || !user.isActive) {
      throw new AppError('ACCOUNT_INACTIVE', 403, 'Cuenta no disponible')
    }

    // Rotación: revocar el token usado antes de emitir el nuevo
    await this.refreshTokenRepo.revoke(record.id)

    const accessToken = this.tokenService.generateAccessToken({ userId: user.id, role: user.role })
    const newRawToken = this.tokenService.generateOpaqueToken()
    const newHash = this.tokenService.hashToken(newRawToken)
    const expiresAt = new Date(Date.now() + this.tokenService.refreshTokenTtlMs())

    await this.refreshTokenRepo.create({ userId: user.id, tokenHash: newHash, expiresAt })

    return { accessToken, refreshToken: newRawToken }
  }
}
