import jwt from '@fastify/jwt'
import Fastify from 'fastify'
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
import { prisma } from '../../lib/prisma.js'
import { registerUserPaymentRoutes } from '../user-payments.js'

const JWT_SECRET = 'test-secret'
const TEST_USER_EMAIL = `test-pay-${Date.now()}@example.com`

let testUserId
let testOrderId

async function signToken(userId, email = TEST_USER_EMAIL) {
  const fastify = Fastify()
  await fastify.register(jwt, { secret: JWT_SECRET })
  await fastify.ready()
  return fastify.jwt.sign({ sub: userId, email })
}

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 registerUserPaymentRoutes(app)
  await app.ready()
  return app
}

describe('POST /api/me/orders/:id/pay', () => {
  let app

  beforeAll(async () => {
    await prisma.payment.deleteMany()
    await prisma.order.deleteMany({ where: { user: { email: TEST_USER_EMAIL } } })
    await prisma.user.deleteMany({ where: { email: TEST_USER_EMAIL } })

    const user = await prisma.user.create({
      data: { email: TEST_USER_EMAIL },
    })
    testUserId = user.id

    const order = await prisma.order.create({
      data: {
        userId: testUserId,
        status: 'PENDING_PAYMENT',
        paymentMethod: 'online',
        deliveryFeeLocked: true,
        totalCents: 100000,
        currency: 'RUB',
        deliveryFeeCents: 0,
      },
    })
    testOrderId = order.id
  })

  afterAll(async () => {
    await prisma.payment.deleteMany({ where: { orderId: testOrderId } })
    await prisma.order.deleteMany({ where: { userId: testUserId } })
    await prisma.user.deleteMany({ where: { email: TEST_USER_EMAIL } })
  })

  beforeEach(async () => {
    await prisma.order.update({
      where: { id: testOrderId },
      data: {
        status: 'PENDING_PAYMENT',
        paymentMethod: 'online',
        deliveryFeeLocked: true,
      },
    })
    app = await buildApp()
  })

  afterEach(async () => {
    await app.close()
    vi.restoreAllMocks()
  })

  it('returns 401 without auth', async () => {
    const res = await app.inject({
      method: 'POST',
      url: `/api/me/orders/${testOrderId}/pay`,
    })
    expect(res.statusCode).toBe(401)
  })

  it('returns 404 when order not found', async () => {
    const token = await signToken(testUserId)
    const res = await app.inject({
      method: 'POST',
      url: '/api/me/orders/nonexistent-id/pay',
      headers: { authorization: `Bearer ${token}` },
    })
    expect(res.statusCode).toBe(404)
  })

  it('returns 409 when payment method is on_pickup', async () => {
    await prisma.order.update({
      where: { id: testOrderId },
      data: { paymentMethod: 'on_pickup' },
    })
    const token = await signToken(testUserId)
    const res = await app.inject({
      method: 'POST',
      url: `/api/me/orders/${testOrderId}/pay`,
      headers: { authorization: `Bearer ${token}` },
    })
    expect(res.statusCode).toBe(409)
  })

  it('returns 409 when order not in PENDING_PAYMENT status', async () => {
    await prisma.order.update({
      where: { id: testOrderId },
      data: { status: 'PAID' },
    })
    const token = await signToken(testUserId)
    const res = await app.inject({
      method: 'POST',
      url: `/api/me/orders/${testOrderId}/pay`,
      headers: { authorization: `Bearer ${token}` },
    })
    expect(res.statusCode).toBe(409)
  })

  it('returns 409 when deliveryFeeLocked is false', async () => {
    await prisma.order.update({
      where: { id: testOrderId },
      data: { deliveryFeeLocked: false },
    })
    const token = await signToken(testUserId)
    const res = await app.inject({
      method: 'POST',
      url: `/api/me/orders/${testOrderId}/pay`,
      headers: { authorization: `Bearer ${token}` },
    })
    expect(res.statusCode).toBe(409)
  })

  it('returns 422 when user has no email', async () => {
    const noEmailUser = await prisma.user.create({
      data: { email: `noemail-${Date.now()}@test.com` },
    })
    const noEmailOrder = await prisma.order.create({
      data: {
        userId: noEmailUser.id,
        status: 'PENDING_PAYMENT',
        paymentMethod: 'online',
        deliveryFeeLocked: true,
        totalCents: 100000,
        currency: 'RUB',
      },
    })

    const fastify = Fastify()
    await fastify.register(jwt, { secret: JWT_SECRET })
    const token = fastify.jwt.sign({ sub: noEmailUser.id })
    await fastify.close()

    const res = await app.inject({
      method: 'POST',
      url: `/api/me/orders/${noEmailOrder.id}/pay`,
      headers: { authorization: `Bearer ${token}` },
    })
    expect(res.statusCode).toBe(422)

    await prisma.order.deleteMany({ where: { userId: noEmailUser.id } })
    await prisma.user.deleteMany({ where: { id: noEmailUser.id } })
  })
})

describe('GET /api/me/orders/:orderId/payment', () => {
  let app
  let getTestUserId
  let getTestOrderId

  beforeAll(async () => {
    const getEmail = `get-pay-${Date.now()}@example.com`
    const user = await prisma.user.create({ data: { email: getEmail } })
    getTestUserId = user.id

    const order = await prisma.order.create({
      data: {
        userId: getTestUserId,
        status: 'PENDING_PAYMENT',
        paymentMethod: 'online',
        deliveryFeeLocked: true,
        totalCents: 100000,
        currency: 'RUB',
      },
    })
    getTestOrderId = order.id
  })

  afterAll(async () => {
    await prisma.payment.deleteMany({ where: { orderId: getTestOrderId } })
    await prisma.order.deleteMany({ where: { userId: getTestUserId } })
    await prisma.user.deleteMany({ where: { id: getTestUserId } })
  })

  beforeEach(async () => {
    app = await buildApp()
  })

  afterEach(async () => {
    await app.close()
  })

  it('returns 401 without auth', async () => {
    const res = await app.inject({
      method: 'GET',
      url: `/api/me/orders/${getTestOrderId}/payment`,
    })
    expect(res.statusCode).toBe(401)
  })

  it('returns 404 when order not found', async () => {
    const token = await signToken(getTestUserId)
    const res = await app.inject({
      method: 'GET',
      url: '/api/me/orders/nonexistent-id/payment',
      headers: { authorization: `Bearer ${token}` },
    })
    expect(res.statusCode).toBe(404)
  })

  it('returns status null when no payment exists', async () => {
    const token = await signToken(getTestUserId)
    const res = await app.inject({
      method: 'GET',
      url: `/api/me/orders/${getTestOrderId}/payment`,
      headers: { authorization: `Bearer ${token}` },
    })
    expect(res.statusCode).toBe(200)
    const body = JSON.parse(res.payload)
    expect(body.status).toBeNull()
    expect(body.paid).toBe(false)
  })
})
