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

vi.mock('../../lib/prisma', () => ({ prisma: {} }))
vi.mock('../../lib/redis', () => ({
  redis: { on: vi.fn(), get: vi.fn(), set: vi.fn(), del: vi.fn(), quit: vi.fn() },
}))
vi.mock('../../middleware/requireCompanyAuth', () => ({
  requireCompanyAuth: (req: any, _res: any, next: any) => {
    req.employee = { id: 'employee_1', role: 'OWNER' }
    next()
  },
  requireCompanyDocumentAuth: (req: any, _res: any, next: any) => {
    req.employee = { id: 'employee_1', role: 'OWNER' }
    next()
  },
}))
vi.mock('../../middleware/requireTenant', () => ({
  requireTenant: (req: any, _res: any, next: any) => {
    req.companyId = 'company_1'
    next()
  },
}))
vi.mock('../../middleware/requireSubscription', () => ({
  requireSubscription: (_req: any, _res: any, next: any) => next(),
}))
vi.mock('../../middleware/requireRole', () => ({
  requireRole: () => (_req: any, _res: any, next: any) => next(),
}))
vi.mock('../../middleware/requireRenterAuth', () => ({
  requireRenterAuth: (req: any, _res: any, next: any) => {
    req.renterId = 'renter_1'
    next()
  },
  optionalRenterAuth: (_req: any, _res: any, next: any) => next(),
}))
vi.mock('../../modules/team/team.service', () => ({
  getMembers: vi.fn(),
  getMemberStats: vi.fn(),
  inviteEmployee: vi.fn(),
  updateEmployeeRole: vi.fn(),
  deactivateEmployee: vi.fn(),
  reactivateEmployee: vi.fn(),
  removeEmployee: vi.fn(),
}))
vi.mock('../../modules/notifications/notification.service', () => ({
  listCompany: vi.fn(),
  countUnread: vi.fn(),
  markRead: vi.fn(),
  markAllRead: vi.fn(),
  getPreferences: vi.fn(),
  setPreferences: vi.fn(),
  listCompanyHistory: vi.fn(),
  listRenter: vi.fn(),
  markRenterRead: vi.fn(),
  markAllRenterRead: vi.fn(),
  getRenterPreferences: vi.fn(),
  setRenterPreferences: vi.fn(),
}))
vi.mock('../../modules/analytics/analytics.service', () => ({
  getSummary: vi.fn(),
  getDashboard: vi.fn(),
  getSources: vi.fn(),
  getReport: vi.fn(),
}))

import request from 'supertest'
import { createApp } from '../../app'
import * as teamService from '../../modules/team/team.service'
import * as notificationService from '../../modules/notifications/notification.service'
import * as analyticsService from '../../modules/analytics/analytics.service'

const app = createApp()

describe('operations validation API contracts', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    vi.mocked(teamService.inviteEmployee).mockResolvedValue({ id: 'employee_2' } as never)
    vi.mocked(teamService.updateEmployeeRole).mockResolvedValue({ id: 'employee_2', role: 'AGENT' } as never)
    vi.mocked(notificationService.setPreferences).mockResolvedValue(undefined as never)
    vi.mocked(notificationService.listCompanyHistory).mockResolvedValue([] as never)
    vi.mocked(analyticsService.getReport).mockResolvedValue({
      format: 'CSV',
      startDate: new Date('2026-01-01T00:00:00.000Z'),
      endDate: new Date('2026-01-31T00:00:00.000Z'),
      csv: 'metric,value\nrevenue,1000\n',
    } as never)
  })

  it('rejects invalid team invite roles before service execution', async () => {
    const res = await request(app).post('/api/v1/team/invite').send({
      firstName: 'Aya',
      lastName: 'Benali',
      email: 'aya@example.test',
      role: 'OWNER',
    })

    expect(res.status).toBe(400)
    expect(teamService.inviteEmployee).not.toHaveBeenCalled()
  })

  it('passes valid team role updates with actor context', async () => {
    const res = await request(app).patch('/api/v1/team/employee_2/role').send({ role: 'AGENT' })

    expect(res.status).toBe(200)
    expect(teamService.updateEmployeeRole).toHaveBeenCalledWith('company_1', 'employee_1', 'OWNER', 'employee_2', { role: 'AGENT' })
  })

  it('rejects malformed company notification preferences before persistence', async () => {
    const res = await request(app).patch('/api/v1/notifications/company/preferences').send([
      { notificationType: 'BOOKING', channel: 'EMAIL' },
    ])

    expect(res.status).toBe(400)
    expect(notificationService.setPreferences).not.toHaveBeenCalled()
  })

  it('accepts valid company notification preferences and passes employee identity', async () => {
    const res = await request(app).patch('/api/v1/notifications/company/preferences').send([
      { notificationType: 'BOOKING_CREATED', channel: 'EMAIL', enabled: true },
    ])

    expect(res.status).toBe(200)
    expect(notificationService.setPreferences).toHaveBeenCalledWith('employee_1', [
      { notificationType: 'BOOKING_CREATED', channel: 'EMAIL', enabled: true },
    ])
  })

  it('coerces notification history limits before querying service history', async () => {
    const res = await request(app).get('/api/v1/notifications/history?channel=EMAIL&status=SENT&limit=50')

    expect(res.status).toBe(200)
    expect(notificationService.listCompanyHistory).toHaveBeenCalledWith('company_1', {
      channel: 'EMAIL',
      status: 'SENT',
      limit: 50,
    })
  })

  it('streams analytics CSV reports with deterministic filenames', async () => {
    const res = await request(app).get('/api/v1/analytics/report?format=CSV&period=month')

    expect(res.status).toBe(200)
    expect(res.header['content-type']).toContain('text/csv')
    expect(res.header['content-disposition']).toContain('report-2026-01-01-2026-01-31.csv')
    expect(res.text).toContain('metric,value')
    expect(analyticsService.getReport).toHaveBeenCalledWith('company_1', { format: 'CSV', period: 'month' })
  })
})
