import { beforeEach, describe, expect, it, vi } from 'vitest'

vi.mock('jsonwebtoken', () => ({
  default: { verify: vi.fn() },
}))

vi.mock('../../lib/redis', () => ({
  redis: { on: vi.fn(), get: vi.fn(), set: vi.fn(), del: vi.fn(), quit: vi.fn() },
}))

vi.mock('../../lib/prisma', () => ({
  prisma: {
    employee: { findUnique: vi.fn() },
    company: { findUnique: vi.fn() },
    subscription: { findUnique: vi.fn() },
    adminUser: { findUnique: vi.fn() },
    renter: { findUnique: vi.fn() },
  },
}))

vi.mock('../../modules/vehicles/vehicle.service', () => ({
  listVehicles: vi.fn(),
  createVehicle: vi.fn(),
}))

vi.mock('../../modules/admin/admin.service', () => ({
  listCompanies: vi.fn(),
  login: vi.fn(),
  forgotPassword: vi.fn(),
  resetPassword: vi.fn(),
}))

import request from 'supertest'
import jwt from 'jsonwebtoken'
import { createApp } from '../../app'
import { prisma } from '../../lib/prisma'
import * as vehicleService from '../../modules/vehicles/vehicle.service'
import * as adminService from '../../modules/admin/admin.service'

const app = createApp()

function activeEmployee(role: string) {
  return {
    id: 'emp_1',
    role,
    isActive: true,
    companyId: 'company_1',
    company: { id: 'company_1', status: 'ACTIVE' },
  }
}

describe('auth middleware API boundaries', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    process.env.JWT_SECRET = 'test-secret'
    process.env.NEXT_PUBLIC_DASHBOARD_URL = 'https://dashboard.example.test'
    vi.mocked(prisma.subscription.findUnique).mockResolvedValue({ status: 'ACTIVE' } as never)
  })

  it('rejects protected company routes before service execution when no token is present', async () => {
    const res = await request(app).get('/api/v1/vehicles')

    expect(res.status).toBe(401)
    expect(res.body).toEqual({ error: 'unauthenticated', message: 'Authentication required', statusCode: 401 })
    expect(vehicleService.listVehicles).not.toHaveBeenCalled()
  })

  it('rejects employee-protected routes when a renter token is used', async () => {
    vi.mocked(jwt.verify).mockReturnValue({ sub: 'renter_1', type: 'renter' } as never)

    const res = await request(app).get('/api/v1/vehicles').set('Authorization', 'Bearer renter-token')

    expect(res.status).toBe(401)
    expect(res.body).toMatchObject({ error: 'invalid_token', message: 'Invalid or expired session token', statusCode: 401 })
    expect(prisma.employee.findUnique).not.toHaveBeenCalled()
  })

  it('rejects inactive employees before tenant and subscription checks', async () => {
    vi.mocked(jwt.verify).mockReturnValue({ sub: 'emp_1', type: 'employee' } as never)
    vi.mocked(prisma.employee.findUnique).mockResolvedValue({ id: 'emp_1', isActive: false } as never)

    const res = await request(app).get('/api/v1/vehicles').set('Authorization', 'Bearer employee-token')

    expect(res.status).toBe(401)
    expect(res.body).toEqual({ error: 'unauthenticated', message: 'Employee account not found or inactive', statusCode: 401 })
    expect(prisma.company.findUnique).not.toHaveBeenCalled()
  })

  it('blocks suspended tenants at the subscription boundary', async () => {
    vi.mocked(jwt.verify).mockReturnValue({ sub: 'emp_1', type: 'employee' } as never)
    vi.mocked(prisma.employee.findUnique).mockResolvedValue(activeEmployee('OWNER') as never)
    vi.mocked(prisma.company.findUnique).mockResolvedValue({ id: 'company_1', status: 'SUSPENDED' } as never)

    const res = await request(app).get('/api/v1/vehicles').set('Authorization', 'Bearer employee-token')

    expect(res.status).toBe(402)
    expect(res.body).toEqual(expect.objectContaining({
      error: 'subscription_suspended',
      billingUrl: 'https://dashboard.example.test/subscription',
    }))
    expect(vehicleService.listVehicles).not.toHaveBeenCalled()
  })

  it('blocks lower company roles from manager-only mutations', async () => {
    vi.mocked(jwt.verify).mockReturnValue({ sub: 'emp_1', type: 'employee' } as never)
    vi.mocked(prisma.employee.findUnique).mockResolvedValue(activeEmployee('AGENT') as never)
    vi.mocked(prisma.company.findUnique).mockResolvedValue({ id: 'company_1', status: 'ACTIVE' } as never)

    const res = await request(app).post('/api/v1/vehicles').set('Authorization', 'Bearer employee-token').send({})

    expect(res.status).toBe(403)
    expect(res.body).toEqual(expect.objectContaining({
      error: 'forbidden',
      requiredRole: 'MANAGER',
      yourRole: 'AGENT',
    }))
    expect(vehicleService.createVehicle).not.toHaveBeenCalled()
  })

  it('allows authenticated active tenants through to read-only company handlers', async () => {
    vi.mocked(jwt.verify).mockReturnValue({ sub: 'emp_1', type: 'employee' } as never)
    vi.mocked(prisma.employee.findUnique).mockResolvedValue(activeEmployee('AGENT') as never)
    vi.mocked(prisma.company.findUnique).mockResolvedValue({ id: 'company_1', status: 'ACTIVE' } as never)
    vi.mocked(vehicleService.listVehicles).mockResolvedValue({ data: [{ id: 'vehicle_1' }], pagination: { page: 1 } } as never)

    const res = await request(app).get('/api/v1/vehicles').set('Authorization', 'Bearer employee-token')

    expect(res.status).toBe(200)
    expect(res.body).toEqual({ data: [{ id: 'vehicle_1' }], pagination: { page: 1 } })
    expect(vehicleService.listVehicles).toHaveBeenCalledWith('company_1', { page: 1, pageSize: 20 })
  })

  it('rejects protected admin routes before admin service execution when no admin token is present', async () => {
    const res = await request(app).get('/api/v1/admin/companies')

    expect(res.status).toBe(401)
    expect(res.body).toEqual({ error: 'unauthenticated', message: 'Admin authentication required', statusCode: 401 })
    expect(adminService.listCompanies).not.toHaveBeenCalled()
  })

  it('allows valid admin tokens through protected admin read routes', async () => {
    vi.mocked(jwt.verify).mockReturnValue({ sub: 'admin_1', type: 'admin' } as never)
    vi.mocked(prisma.adminUser.findUnique).mockResolvedValue({ id: 'admin_1', isActive: true, role: 'SUPPORT', totpEnabled: true } as never)
    vi.mocked(adminService.listCompanies).mockResolvedValue({ data: [{ id: 'company_1' }], pagination: { page: 1 } } as never)

    const res = await request(app).get('/api/v1/admin/companies').set('Authorization', 'Bearer admin-token')

    expect(res.status).toBe(200)
    expect(res.body).toEqual({ data: { data: [{ id: 'company_1' }], pagination: { page: 1 } } })
    expect(adminService.listCompanies).toHaveBeenCalledWith({ page: 1, pageSize: 20 })
  })
})
