import User from '#models/user'
import RefreshToken from '#models/refresh_token'
import PasswordResetNotification from '#mails/password_reset_notification'
import type { HttpContext } from '@adonisjs/core/http'
import mail from '@adonisjs/mail/services/main'
import encryption from '@adonisjs/core/services/encryption'
import env from '#start/env'
import vine from '@vinejs/vine'
import { DateTime } from 'luxon'

const requestValidator = vine.compile(
  vine.object({
    email: vine.string().email(),
    locale: vine.string().fixedLength(2).alpha().optional(),
  })
)

const confirmValidator = vine.compile(
  vine.object({
    token: vine.string(),
    password: vine.string().minLength(8).maxLength(72),
    passwordConfirmation: vine.string().sameAs('password'),
  })
)

export default class PasswordResetController {
  /**
   * Step 1 — Request a password reset link.
   * Always returns 200 to prevent email enumeration.
   */
  async request({ request, response }: HttpContext) {
    const { email, locale } = await request.validateUsing(requestValidator)

    const user = await User.findBy('email', email)

    if (user && user.status !== 'banned') {
      const token = encryption.encrypt(
        { userId: user.id, expiresAt: DateTime.now().plus({ hours: 1 }).toISO() },
        '1h'
      )

      const resetUrl = `${env.get('FRONTEND_URL')}/reset-password?token=${encodeURIComponent(token)}`
      await mail.send(new PasswordResetNotification(user, resetUrl, locale ?? 'en'))
    }

    return response.ok({
      message: 'If that email is registered you will receive a reset link shortly.',
    })
  }

  /**
   * Step 2 — Confirm the reset with a new password.
   */
  async confirm({ request, response }: HttpContext) {
    const { token, password } = await request.validateUsing(confirmValidator)

    const payload = encryption.decrypt<{ userId: string; expiresAt: string }>(token)

    if (!payload || DateTime.fromISO(payload.expiresAt) < DateTime.now()) {
      return response.badRequest({ message: 'Invalid or expired reset token.' })
    }

    const user = await User.find(payload.userId)

    if (!user || user.status === 'banned') {
      return response.badRequest({ message: 'Invalid or expired reset token.' })
    }

    // Update password and record when it changed (invalidates all existing JWTs)
    user.password = password
    user.passwordChangedAt = DateTime.now()
    await user.save()

    // Revoke all refresh tokens — forces re-login on all devices
    await RefreshToken.query().where('user_id', user.id).whereNull('revoked_at').update({
      revoked_at: DateTime.now().toUTC().toFormat('yyyy-MM-dd HH:mm:ss'),
    })

    return response.ok({ message: 'Password reset successfully. Please sign in.' })
  }
}
