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('./customer.repo')
vi.mock('../../services/licenseValidationService', () => ({
  validateAndFlagLicense: vi.fn().mockResolvedValue({ status: 'VALID' }),
}))
vi.mock('../../lib/storage', () => ({
  uploadImage: vi.fn().mockResolvedValue('http://localhost:4000/storage/companies/company_1/customers/customer_1/new-license.jpg'),
  deleteImage: vi.fn().mockResolvedValue(undefined),
  resolveStoredFilePath: vi.fn().mockReturnValue('/tmp/license.jpg'),
  getProtectedCustomerLicenseImageUrl: vi.fn((customerId: string) => `http://localhost:3000/dashboard/api/v1/customers/${customerId}/license-image`),
}))

import * as repo from './customer.repo'
import * as service from './customer.service'
import { validateAndFlagLicense } from '../../services/licenseValidationService'
import { deleteImage, resolveStoredFilePath, uploadImage } from '../../lib/storage'

const customer = {
  id: 'customer_1',
  companyId: 'company_1',
  firstName: 'Aya',
  lastName: 'Benali',
  email: 'aya@example.test',
  licenseImageUrl: null,
  flagged: false,
  flagReason: null,
}

beforeEach(() => {
  vi.clearAllMocks()
  vi.mocked(resolveStoredFilePath).mockReturnValue('/tmp/license.jpg')
})

describe('customer service edge cases', () => {
  it('trims and caps customer search text before building the query', async () => {
    vi.mocked(repo.findMany).mockResolvedValue([[], 0] as any)
    const longQuery = `  ${'x'.repeat(150)}  `

    await service.listCustomers('company_1', { q: longQuery, page: 2, pageSize: 10 })

    const [where, skip, take] = vi.mocked(repo.findMany).mock.calls[0]
    expect(skip).toBe(10)
    expect(take).toBe(10)
    expect(where.OR[0].firstName.contains).toHaveLength(100)
    expect(where.OR[0].firstName.contains).not.toMatch(/^\s|\s$/)
  })

  it('parses customer date fields and triggers async license validation on create', async () => {
    vi.mocked(repo.create).mockResolvedValue({ ...customer, licenseExpiry: new Date('2028-01-01T00:00:00.000Z') } as any)

    await service.createCustomer({
      firstName: 'Aya',
      lastName: 'Benali',
      email: 'aya@example.test',
      dateOfBirth: '1998-05-20',
      licenseIssuedAt: '2024-01-01',
      licenseExpiry: '2028-01-01',
    }, 'company_1')

    expect(repo.create).toHaveBeenCalledWith(expect.objectContaining({
      companyId: 'company_1',
      dateOfBirth: expect.any(Date),
      licenseIssuedAt: expect.any(Date),
      licenseExpiry: expect.any(Date),
    }))
    expect(validateAndFlagLicense).toHaveBeenCalledWith('customer_1', 'company_1')
  })

  it('parses changed license dates and validates refreshed customer licenses on update', async () => {
    vi.mocked(repo.updateMany).mockResolvedValue({ count: 1 } as any)
    vi.mocked(repo.findUniqueOrThrow).mockResolvedValue({ ...customer, licenseExpiry: new Date('2029-01-01T00:00:00.000Z') } as any)

    await service.updateCustomer('customer_1', 'company_1', { licenseExpiry: '2029-01-01' })

    expect(repo.updateMany).toHaveBeenCalledWith('customer_1', 'company_1', expect.objectContaining({
      licenseExpiry: expect.any(Date),
      dateOfBirth: undefined,
      licenseIssuedAt: undefined,
    }))
    expect(validateAndFlagLicense).toHaveBeenCalledWith('customer_1', 'company_1')
  })

  it('replaces existing license images without failing the upload when old-file deletion rejects', async () => {
    vi.mocked(repo.findByIdSimple).mockResolvedValue({ ...customer, licenseImageUrl: 'http://localhost:4000/storage/old-license.jpg' } as any)
    vi.mocked(repo.updateById).mockResolvedValue({ ...customer, licenseImageUrl: 'http://localhost:4000/storage/companies/company_1/customers/customer_1/new-license.jpg' } as any)
    vi.mocked(deleteImage).mockRejectedValue(new Error('old file missing'))

    const result = await service.uploadLicenseImage('customer_1', 'company_1', { buffer: Buffer.from('image') } as any)

    expect(uploadImage).toHaveBeenCalledWith(expect.any(Buffer), 'companies/company_1/customers/customer_1')
    expect(deleteImage).toHaveBeenCalledWith('http://localhost:4000/storage/old-license.jpg')
    expect(result.licenseImageUrl).toBe('http://localhost:3000/dashboard/api/v1/customers/customer_1/license-image')
  })

  it('throws not found when a stored license URL cannot be resolved to a safe file path', async () => {
    vi.mocked(repo.findByIdSimple).mockResolvedValue({ ...customer, licenseImageUrl: 'https://evil.example/license.jpg' } as any)
    vi.mocked(resolveStoredFilePath).mockReturnValue(null)

    await expect(service.getLicenseImageFile('customer_1', 'company_1')).rejects.toThrow('License image not found')
  })

  it('records approver name, timestamp, and note when denying a license', async () => {
    vi.mocked(repo.findByIdSimple).mockResolvedValue(customer as any)
    vi.mocked(repo.updateById).mockResolvedValue({ ...customer, licenseValidationStatus: 'DENIED' } as any)

    await service.approveLicense('customer_1', 'company_1', 'DENY', 'Unreadable scan', 'Ops Lead')

    expect(repo.updateById).toHaveBeenCalledWith('customer_1', expect.objectContaining({
      licenseValidationStatus: 'DENIED',
      licenseApprovedBy: 'Ops Lead',
      licenseApprovedAt: expect.any(Date),
      licenseApprovalNote: 'Unreadable scan',
    }))
  })
})
