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: { adminUser: { findUnique: vi.fn() } },
}))
vi.mock('../../modules/menu/menu.service', () => ({
  listMenuItems: vi.fn(),
  createMenuItem: vi.fn(),
  getMenuItem: vi.fn(),
  updateMenuItem: vi.fn(),
  setMenuItemStatus: vi.fn(),
  deleteMenuItem: vi.fn(),
  assignMenuItemToPlans: vi.fn(),
  removeMenuItemFromPlan: vi.fn(),
  assignMenuItemToCompanies: vi.fn(),
  removeMenuItemFromCompany: vi.fn(),
  previewCompanyMenu: vi.fn(),
  listMenuAuditLogs: vi.fn(),
}))

import request from 'supertest'
import jwt from 'jsonwebtoken'
import { createApp } from '../../app'
import { prisma } from '../../lib/prisma'
import * as menuService from '../../modules/menu/menu.service'

const app = createApp()

function admin(role = 'ADMIN') {
  return { id: 'admin_1', email: 'admin@example.test', role, isActive: true, totpEnabled: true }
}

describe('admin menu API contracts', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    process.env.JWT_SECRET = 'test-secret'
    vi.mocked(jwt.verify).mockReturnValue({ sub: 'admin_1', type: 'admin' } as never)
    vi.mocked(prisma.adminUser.findUnique).mockResolvedValue(admin() as never)
  })

  it('rejects menu item creation with blank labels before service execution', async () => {
    const res = await request(app)
      .post('/api/v1/admin/menu-items')
      .set('Authorization', 'Bearer admin-token')
      .send({ label: '   ', itemType: 'INTERNAL_PAGE', routeOrUrl: '/fleet' })

    expect(res.status).toBe(400)
    expect(menuService.createMenuItem).not.toHaveBeenCalled()
  })

  it('passes normalized menu item creation payload with admin actor', async () => {
    vi.mocked(menuService.createMenuItem).mockResolvedValue({ id: 'item_1', label: 'Fleet' } as never)

    const res = await request(app)
      .post('/api/v1/admin/menu-items')
      .set('Authorization', 'Bearer admin-token')
      .send({ label: ' Fleet ', itemType: 'INTERNAL_PAGE', routeOrUrl: '/fleet', roles: ['MANAGER'] })

    expect(res.status).toBe(201)
    expect(menuService.createMenuItem).toHaveBeenCalledWith(
      expect.objectContaining({ label: 'Fleet', itemType: 'INTERNAL_PAGE', routeOrUrl: '/fleet', roles: ['MANAGER'] }),
      expect.objectContaining({ id: 'admin_1' }),
    )
  })

  it('rejects invalid menu status bodies before mutation', async () => {
    const res = await request(app)
      .patch('/api/v1/admin/menu-items/item_1/status')
      .set('Authorization', 'Bearer admin-token')
      .send({ isActive: 'false' })

    expect(res.status).toBe(400)
    expect(menuService.setMenuItemStatus).not.toHaveBeenCalled()
  })

  it('requires admin privileges for menu mutations while support can preview', async () => {
    vi.mocked(prisma.adminUser.findUnique).mockResolvedValue(admin('SUPPORT') as never)
    vi.mocked(menuService.previewCompanyMenu).mockResolvedValue({ items: [], finalMenu: [] } as never)

    const createRes = await request(app)
      .post('/api/v1/admin/menu-items')
      .set('Authorization', 'Bearer admin-token')
      .send({ label: 'Fleet', itemType: 'INTERNAL_PAGE', routeOrUrl: '/fleet' })
    expect(createRes.status).toBe(403)

    const previewRes = await request(app)
      .post('/api/v1/admin/menu-preview')
      .set('Authorization', 'Bearer admin-token')
      .send({ companyId: 'company_1', role: 'MANAGER' })
    expect(previewRes.status).toBe(200)
    expect(menuService.previewCompanyMenu).toHaveBeenCalledWith({ companyId: 'company_1', role: 'MANAGER' })
  })

  it('coerces audit-log pagination defaults and caps oversized limits', async () => {
    vi.mocked(menuService.listMenuAuditLogs).mockResolvedValue({ data: [], total: 0, page: 1, pageSize: 20, totalPages: 0 } as never)

    const defaultRes = await request(app)
      .get('/api/v1/admin/menu-audit-logs')
      .set('Authorization', 'Bearer admin-token')
    expect(defaultRes.status).toBe(200)
    expect(menuService.listMenuAuditLogs).toHaveBeenCalledWith({ page: 1, pageSize: 20 })

    const oversizedRes = await request(app)
      .get('/api/v1/admin/menu-audit-logs?pageSize=500')
      .set('Authorization', 'Bearer admin-token')
    expect(oversizedRes.status).toBe(400)
  })
})
