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

vi.mock('./offer.repo', () => ({
  findMany: vi.fn(),
  findByIdOrThrow: vi.fn(),
  create: vi.fn(),
  findFirst: vi.fn(),
  update: vi.fn(),
  deleteMany: vi.fn(),
  setActive: vi.fn(),
  getStats: vi.fn(),
}))

import { NotFoundError } from '../../http/errors'
import * as repo from './offer.repo'
import { createOffer, deleteOffer, getOffer, getOfferStats, listOffers, setOfferActive, updateOffer } from './offer.service'

describe('offer.service', () => {
  beforeEach(() => vi.clearAllMocks())

  it('keeps listing filters tenant-scoped and delegates them unchanged to the repository', async () => {
    vi.mocked(repo.findMany).mockResolvedValue([{ id: 'offer_1' }] as never)

    const result = await listOffers('company_1', { active: 'true', public: 'false' })

    expect(repo.findMany).toHaveBeenCalledWith('company_1', { active: 'true', public: 'false' })
    expect(result).toEqual([{ id: 'offer_1' }])
  })

  it('creates offers with separate offer data and vehicle assignment ids', async () => {
    vi.mocked(repo.create).mockResolvedValue({ id: 'offer_1' } as never)

    const payload = { title: 'Weekend deal', type: 'PERCENTAGE', discountValue: 15 }
    await createOffer('company_1', payload, ['vehicle_1', 'vehicle_2'])

    expect(repo.create).toHaveBeenCalledWith('company_1', payload, ['vehicle_1', 'vehicle_2'])
  })

  it('throws NotFoundError before updating an offer outside the tenant boundary', async () => {
    vi.mocked(repo.findFirst).mockResolvedValue(null as never)

    await expect(updateOffer('offer_1', 'company_1', { title: 'Nope' })).rejects.toBeInstanceOf(NotFoundError)
    expect(repo.update).not.toHaveBeenCalled()
  })

  it('updates an existing offer and preserves explicit vehicle assignment updates', async () => {
    vi.mocked(repo.findFirst).mockResolvedValue({ id: 'offer_1' } as never)
    vi.mocked(repo.update).mockResolvedValue({ id: 'offer_1', title: 'Summer' } as never)

    const result = await updateOffer('offer_1', 'company_1', { title: 'Summer' }, [])

    expect(repo.update).toHaveBeenCalledWith('offer_1', { title: 'Summer' }, [])
    expect(result).toEqual({ id: 'offer_1', title: 'Summer' })
  })

  it('activates, deactivates, deletes, and reads stats through tenant-scoped repository methods', async () => {
    vi.mocked(repo.setActive).mockResolvedValue({ id: 'offer_1', isActive: true } as never)
    vi.mocked(repo.deleteMany).mockResolvedValue({ count: 1 } as never)
    vi.mocked(repo.getStats).mockResolvedValue({ redemptions: 3 } as never)
    vi.mocked(repo.findByIdOrThrow).mockResolvedValue({ id: 'offer_1' } as never)

    await getOffer('offer_1', 'company_1')
    await setOfferActive('offer_1', 'company_1', true)
    await deleteOffer('offer_1', 'company_1')
    await getOfferStats('offer_1', 'company_1')

    expect(repo.findByIdOrThrow).toHaveBeenCalledWith('offer_1', 'company_1')
    expect(repo.setActive).toHaveBeenCalledWith('offer_1', 'company_1', true)
    expect(repo.deleteMany).toHaveBeenCalledWith('offer_1', 'company_1')
    expect(repo.getStats).toHaveBeenCalledWith('offer_1', 'company_1')
  })
})
