import jwt from '@fastify/jwt'
import Fastify from 'fastify'
import { afterAll, beforeEach, beforeAll, describe, expect, it } from 'vitest'
import { prisma } from '../../lib/prisma.js'
import { registerAuthPasswordRoutes } from '../auth-password.js'

const JWT_SECRET = 'test-secret'

async function buildApp() {
  const app = Fastify({ logger: false })
  await app.register(jwt, { secret: JWT_SECRET })
  app.decorate('authenticate', async function (request, reply) {
    try {
      await request.jwtVerify()
    } catch {
      return reply.code(401).send({ error: 'Unauthorized' })
    }
  })
  app.decorate('eventBus', { emit: () => {} })
  await registerAuthPasswordRoutes(app)
  await app.ready()
  return app
}

function signToken(app, userId, email) {
  return app.jwt.sign({ sub: userId, email })
}

async function createUser(email) {
  const user = await prisma.user.create({
    data: { email, displayName: 'Test', avatar: null, avatarStyle: 'avataaars' },
  })
  await prisma.notificationPreference.create({ data: { userId: user.id, globalEnabled: true } })
  return user
}

describe('POST /api/me/password', () => {
  let app, user, token
  const email = `test-set-pw-${Date.now()}@example.com`

  beforeAll(async () => {
    app = await buildApp()
  })
  afterAll(async () => {
    await prisma.notificationPreference.deleteMany({ where: { userId: user?.id } })
    await prisma.user.deleteMany({ where: { email } })
    await app.close()
  })

  beforeEach(async () => {
    await prisma.notificationPreference.deleteMany({ where: { user: { email } } })
    await prisma.user.deleteMany({ where: { email } })
    user = await createUser(email)
    token = signToken(app, user.id, email)
  })

  it('sets password', async () => {
    const res = await app.inject({
      method: 'POST',
      url: '/api/me/password',
      headers: { authorization: `Bearer ${token}` },
      payload: { password: 'Test123!@' },
    })
    expect(res.statusCode).toBe(200)

    const u = await prisma.user.findUnique({ where: { id: user.id } })
    expect(u.passwordHash).toBeTruthy()
  })

  it('rejects if password already set', async () => {
    await prisma.user.update({ where: { id: user.id }, data: { passwordHash: 'existing' } })
    const res = await app.inject({
      method: 'POST',
      url: '/api/me/password',
      headers: { authorization: `Bearer ${token}` },
      payload: { password: 'Test123!@' },
    })
    expect(res.statusCode).toBe(409)
  })
})

describe('POST /api/me/change-password', () => {
  let app, user, token
  const email = `test-change-pw-${Date.now()}@example.com`

  beforeAll(async () => {
    app = await buildApp()
  })
  afterAll(async () => {
    await prisma.notificationPreference.deleteMany({ where: { userId: user?.id } })
    await prisma.user.deleteMany({ where: { email } })
    await app.close()
  })

  beforeEach(async () => {
    await prisma.notificationPreference.deleteMany({ where: { user: { email } } })
    await prisma.user.deleteMany({ where: { email } })
    user = await createUser(email)
    token = signToken(app, user.id, email)
  })

  it('changes password', async () => {
    await prisma.user.update({ where: { id: user.id }, data: { passwordHash: 'oldhash' } })
    const res = await app.inject({
      method: 'POST',
      url: '/api/me/change-password',
      headers: { authorization: `Bearer ${token}` },
      payload: { oldPassword: 'OldPass1!', newPassword: 'NewPass2@' },
    })
    expect(res.statusCode).toBe(401)

    const u = await prisma.user.findUnique({ where: { id: user.id } })
    expect(u.passwordHash).toBe('oldhash')
  })

  it('rejects if no password set', async () => {
    const res = await app.inject({
      method: 'POST',
      url: '/api/me/change-password',
      headers: { authorization: `Bearer ${token}` },
      payload: { oldPassword: 'OldPass1!', newPassword: 'NewPass2@' },
    })
    expect(res.statusCode).toBe(400)
  })
})
