import RefreshToken from '#models/refresh_token'
import UserTransformer from '#transformers/user_transformer'
import {
  issueTokenPair,
  setRefreshTokenCookie,
  clearRefreshTokenCookie,
  isNativeClient,
} from '#controllers/access_tokens_controller'
import type { HttpContext } from '@adonisjs/core/http'
import { createHash } from 'node:crypto'
import { DateTime } from 'luxon'

export default class RefreshTokensController {
  async store({ request, response, serialize }: HttpContext) {
    const rawToken: string | undefined =
      request.cookie('vtrap_rt') ?? request.input('refreshToken')

    if (!rawToken) {
      return response.unauthorized({ message: 'No refresh token provided' })
    }

    const tokenHash = createHash('sha256').update(rawToken).digest('hex')

    // Load the token regardless of revocation status so we can detect reuse
    const storedToken = await RefreshToken.query()
      .where('token_hash', tokenHash)
      .preload('user', (q) => q.preload('userPref').preload('roles'))
      .first()

    if (!storedToken) {
      clearRefreshTokenCookie(response)
      return response.unauthorized({ message: 'Invalid or expired refresh token' })
    }

    // Reuse detection: token was already rotated — attacker is replaying an old token
    if (storedToken.revokedAt) {
      await RefreshToken.query()
        .where('user_id', storedToken.userId)
        .update({ revoked_at: DateTime.now().toUTC().toFormat('yyyy-MM-dd HH:mm:ss') })
      clearRefreshTokenCookie(response)
      return response.unauthorized({ message: 'Token reuse detected. All sessions revoked.' })
    }

    // Expired but not revoked (normal expiry)
    if (storedToken.expiresAt <= DateTime.now()) {
      clearRefreshTokenCookie(response)
      return response.unauthorized({ message: 'Invalid or expired refresh token' })
    }

    // Rotate: revoke old token and issue a fresh pair
    await storedToken.merge({ revokedAt: DateTime.now() }).save()

    const { accessToken, refreshToken: newRawToken } = await issueTokenPair(storedToken.user)
    setRefreshTokenCookie(response, newRawToken)

    return serialize({
      user: UserTransformer.transform(storedToken.user),
      accessToken,
      ...(isNativeClient(request) ? { refreshToken: newRawToken } : {}),
    })
  }
}
