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', () => ({
  requireCompanyDocumentAuth: (_req: any, _res: any, next: any) => next(),
  requireCompanyAuth: (req: any, _res: any, next: any) => {
    req.employee = { id: 'employee_manager', role: 'MANAGER', firstName: 'Mina', lastName: 'Manager' }
    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('../../modules/offers/offer.service', () => ({
  listOffers: vi.fn(),
  createOffer: vi.fn(),
  getOffer: vi.fn(),
  updateOffer: vi.fn(),
  deleteOffer: vi.fn(),
  setOfferActive: vi.fn(),
  getOfferStats: vi.fn(),
}))
vi.mock('../../modules/reviews/review.service', () => ({
  listReviews: vi.fn(),
  getStats: vi.fn(),
  getReview: vi.fn(),
  replyToReview: vi.fn(),
  sendReviewReminder: vi.fn(),
}))
vi.mock('../../modules/complaints/complaint.service', () => ({
  listComplaints: vi.fn(),
  createComplaint: vi.fn(),
  getComplaint: vi.fn(),
  updateComplaint: vi.fn(),
  deleteComplaint: vi.fn(),
}))

import request from 'supertest'
import { createApp } from '../../app'
import * as offerService from '../../modules/offers/offer.service'
import * as reviewService from '../../modules/reviews/review.service'
import * as complaintService from '../../modules/complaints/complaint.service'

const app = createApp()

describe('feedback and offer API boundary contracts', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    vi.mocked(offerService.createOffer).mockResolvedValue({ id: 'offer_1' } as never)
    vi.mocked(reviewService.listReviews).mockResolvedValue({ items: [], total: 0 } as never)
    vi.mocked(reviewService.replyToReview).mockResolvedValue({ id: 'review_1', companyReply: 'Thanks' } as never)
    vi.mocked(complaintService.createComplaint).mockResolvedValue({ id: 'complaint_1' } as never)
    vi.mocked(complaintService.updateComplaint).mockResolvedValue({ id: 'complaint_1', status: 'RESOLVED' } as never)
  })

  it('normalizes review list pagination and rating before service execution', async () => {
    const res = await request(app).get('/api/v1/reviews?rating=5&page=2&pageSize=15')

    expect(res.status).toBe(200)
    expect(reviewService.listReviews).toHaveBeenCalledWith('company_1', { rating: 5, page: 2, pageSize: 15 })
  })

  it('rejects empty company review replies before service execution', async () => {
    const res = await request(app).patch('/api/v1/reviews/review_1/reply').send({ companyReply: '' })

    expect(res.status).toBe(400)
    expect(reviewService.replyToReview).not.toHaveBeenCalled()
  })

  it('defaults complaint severity and forwards company context', async () => {
    const res = await request(app).post('/api/v1/complaints').send({
      category: 'BILLING',
      subject: 'Incorrect charge',
      customerId: 'customer_1',
    })

    expect(res.status).toBe(201)
    expect(complaintService.createComplaint).toHaveBeenCalledWith('company_1', {
      category: 'BILLING',
      subject: 'Incorrect charge',
      customerId: 'customer_1',
      severity: 'LEVEL_1',
    })
  })

  it('rejects invalid complaint transitions before service execution', async () => {
    const res = await request(app).patch('/api/v1/complaints/complaint_1').send({ status: 'ESCALATED' })

    expect(res.status).toBe(400)
    expect(complaintService.updateComplaint).not.toHaveBeenCalled()
  })

  it('splits offer vehicle ids from the service payload after validation defaults are applied', async () => {
    const res = await request(app).post('/api/v1/offers').send({
      title: 'Weekend deal',
      type: 'PERCENTAGE',
      discountValue: 15,
      appliesToAll: false,
      validFrom: '2026-09-01T00:00:00.000Z',
      validUntil: '2026-09-30T23:59:59.000Z',
      vehicleIds: ['vehicle_1'],
    })

    expect(res.status).toBe(201)
    expect(offerService.createOffer).toHaveBeenCalledWith('company_1', {
      title: 'Weekend deal',
      type: 'PERCENTAGE',
      discountValue: 15,
      appliesToAll: false,
      categories: [],
      validFrom: '2026-09-01T00:00:00.000Z',
      validUntil: '2026-09-30T23:59:59.000Z',
      isActive: true,
      isPublic: true,
      isFeatured: false,
    }, ['vehicle_1'])
  })

  it('rejects offers with non-datetime validity windows before service execution', async () => {
    const res = await request(app).post('/api/v1/offers').send({
      title: 'Bad dates',
      type: 'FIXED_AMOUNT',
      discountValue: 100,
      validFrom: 'tomorrow',
      validUntil: 'next week',
    })

    expect(res.status).toBe(400)
    expect(offerService.createOffer).not.toHaveBeenCalled()
  })
})
