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

vi.mock('./complaint.repo', () => ({
  findMany: vi.fn(),
  findById: vi.fn(),
  create: vi.fn(),
  updateById: vi.fn(),
  deleteById: vi.fn(),
}))

import { NotFoundError } from '../../http/errors'
import * as repo from './complaint.repo'
import { createComplaint, deleteComplaint, getComplaint, listComplaints, updateComplaint } from './complaint.service'

describe('complaint.service', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    vi.setSystemTime(new Date('2026-06-08T12:00:00.000Z'))
  })

  it('builds filtered paginated complaint queries without leaking transport concerns into the repo', async () => {
    vi.mocked(repo.findMany).mockResolvedValue([[{ id: 'complaint_1' }], 11] as never)

    const result = await listComplaints('company_1', {
      status: 'OPEN',
      severity: 'HIGH',
      category: 'BILLING',
      page: 3,
      pageSize: 5,
    })

    expect(repo.findMany).toHaveBeenCalledWith('company_1', {
      status: 'OPEN',
      severity: 'HIGH',
      category: 'BILLING',
    }, 10, 5)
    expect(result).toEqual({
      data: [{ id: 'complaint_1' }],
      meta: { total: 11, page: 3, pageSize: 5, totalPages: 3 },
    })
  })

  it('throws NotFoundError when a complaint cannot be found in the company tenant', async () => {
    vi.mocked(repo.findById).mockResolvedValue(null as never)

    await expect(getComplaint('complaint_1', 'company_1')).rejects.toBeInstanceOf(NotFoundError)
  })

  it('sets resolvedAt exactly when the complaint transitions into RESOLVED', async () => {
    vi.mocked(repo.findById).mockResolvedValue({ id: 'complaint_1', status: 'OPEN' } as never)
    vi.mocked(repo.updateById).mockResolvedValue({ id: 'complaint_1', status: 'RESOLVED' } as never)

    await updateComplaint('complaint_1', 'company_1', { status: 'RESOLVED', resolution: 'Refund issued' })

    expect(repo.updateById).toHaveBeenCalledWith('complaint_1', {
      status: 'RESOLVED',
      resolution: 'Refund issued',
      resolvedAt: new Date('2026-06-08T12:00:00.000Z'),
    })
  })

  it('does not overwrite resolvedAt when an already resolved complaint is edited', async () => {
    vi.mocked(repo.findById).mockResolvedValue({ id: 'complaint_1', status: 'RESOLVED' } as never)

    await updateComplaint('complaint_1', 'company_1', { notes: 'Follow-up call logged' })

    expect(repo.updateById).toHaveBeenCalledWith('complaint_1', { notes: 'Follow-up call logged' })
  })

  it('creates and deletes complaints through tenant-scoped repository calls', async () => {
    vi.mocked(repo.create).mockResolvedValue({ id: 'complaint_1' } as never)
    vi.mocked(repo.findById).mockResolvedValue({ id: 'complaint_1' } as never)

    await createComplaint('company_1', {
      reservationId: 'reservation_1',
      severity: 'MEDIUM',
      category: 'SERVICE',
      subject: 'Late pickup',
    })
    const deleted = await deleteComplaint('complaint_1', 'company_1')

    expect(repo.create).toHaveBeenCalledWith(expect.objectContaining({ companyId: 'company_1', reservationId: 'reservation_1' }))
    expect(repo.deleteById).toHaveBeenCalledWith('complaint_1')
    expect(deleted).toEqual({ success: true })
  })
})
