import { describe, it, expect, vi, beforeEach } from 'vitest'
import * as repo from './customer.repo'
import * as service from './customer.service'

vi.mock('./customer.repo')
vi.mock('../../services/licenseValidationService', () => ({
  validateAndFlagLicense: vi.fn().mockResolvedValue({ status: 'VALID', daysUntilExpiry: 365, requiresApproval: false, message: 'Valid' }),
}))
vi.mock('../../lib/storage', () => ({
  uploadImage: vi.fn().mockResolvedValue('http://localhost:4000/storage/test.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`),
}))

const mockCustomer = {
  id: 'cust_1',
  companyId: 'comp_1',
  firstName: 'John',
  lastName: 'Doe',
  email: 'john@example.com',
  licenseImageUrl: null,
  flagged: false,
  flagReason: null,
  licenseExpiry: null,
}

beforeEach(() => {
  vi.clearAllMocks()
})

describe('customer.service', () => {
  describe('listCustomers', () => {
    it('returns paginated customer list', async () => {
      vi.mocked(repo.findMany).mockResolvedValue([[mockCustomer], 1] as any)
      const result = await service.listCustomers('comp_1', { page: 1, pageSize: 20 })
      expect(result.total).toBe(1)
      expect(result.data).toHaveLength(1)
      expect(result.totalPages).toBe(1)
    })

    it('filters by search query', async () => {
      vi.mocked(repo.findMany).mockResolvedValue([[], 0] as any)
      await service.listCustomers('comp_1', { page: 1, pageSize: 20, q: 'john' })
      expect(repo.findMany).toHaveBeenCalledWith(
        expect.objectContaining({ OR: expect.arrayContaining([expect.objectContaining({ firstName: expect.anything() })]) }),
        0, 20,
      )
    })

    it('filters by flagged status', async () => {
      vi.mocked(repo.findMany).mockResolvedValue([[], 0] as any)
      await service.listCustomers('comp_1', { page: 1, pageSize: 20, flagged: 'true' })
      expect(repo.findMany).toHaveBeenCalledWith(
        expect.objectContaining({ flagged: true }),
        0, 20,
      )
    })
  })

  describe('createCustomer', () => {
    it('creates customer and returns it', async () => {
      vi.mocked(repo.create).mockResolvedValue(mockCustomer as any)
      const result = await service.createCustomer({ firstName: 'John', lastName: 'Doe', email: 'john@example.com' }, 'comp_1')
      expect(result).toEqual({
        ...mockCustomer,
        licenseImageUrl: null,
      })
      expect(repo.create).toHaveBeenCalledWith(expect.objectContaining({ companyId: 'comp_1' }))
    })
  })

  describe('updateCustomer', () => {
    it('throws NotFoundError when customer does not belong to company', async () => {
      vi.mocked(repo.updateMany).mockResolvedValue({ count: 0 } as any)
      await expect(service.updateCustomer('cust_1', 'comp_1', { firstName: 'Jane' })).rejects.toThrow('Customer not found')
    })

    it('updates customer and returns refreshed record', async () => {
      vi.mocked(repo.updateMany).mockResolvedValue({ count: 1 } as any)
      vi.mocked(repo.findUniqueOrThrow).mockResolvedValue({ ...mockCustomer, firstName: 'Jane' } as any)
      const result = await service.updateCustomer('cust_1', 'comp_1', { firstName: 'Jane' })
      expect(result.firstName).toBe('Jane')
      expect(result.licenseImageUrl).toBeNull()
    })
  })

  describe('flagCustomer', () => {
    it('sets flagged=true with reason', async () => {
      vi.mocked(repo.updateMany).mockResolvedValue({ count: 1 } as any)
      await service.flagCustomer('cust_1', 'comp_1', 'Suspicious behavior')
      expect(repo.updateMany).toHaveBeenCalledWith('cust_1', 'comp_1', { flagged: true, flagReason: 'Suspicious behavior' })
    })
  })

  describe('unflagCustomer', () => {
    it('clears flag and reason', async () => {
      vi.mocked(repo.updateMany).mockResolvedValue({ count: 1 } as any)
      await service.unflagCustomer('cust_1', 'comp_1')
      expect(repo.updateMany).toHaveBeenCalledWith('cust_1', 'comp_1', { flagged: false, flagReason: null })
    })
  })

  describe('uploadLicenseImage', () => {
    it('uploads image and updates customer record', async () => {
      vi.mocked(repo.findByIdSimple).mockResolvedValue(mockCustomer as any)
      vi.mocked(repo.updateById).mockResolvedValue({ ...mockCustomer, licenseImageUrl: 'http://localhost:4000/storage/test.jpg' } as any)
      const result = await service.uploadLicenseImage('cust_1', 'comp_1', { buffer: Buffer.from(''), mimetype: 'image/jpeg' } as any)
      expect(result.licenseImageUrl).toBe('http://localhost:3000/dashboard/api/v1/customers/cust_1/license-image')
    })

    it('resolves a stored file path for protected downloads', async () => {
      vi.mocked(repo.findByIdSimple).mockResolvedValue({ ...mockCustomer, licenseImageUrl: 'http://localhost:4000/storage/test.jpg' } as any)
      const result = await service.getLicenseImageFile('cust_1', 'comp_1')
      expect(result).toBe('/tmp/license.jpg')
    })
  })

  describe('approveLicense', () => {
    it('sets APPROVED status', async () => {
      vi.mocked(repo.findByIdSimple).mockResolvedValue(mockCustomer as any)
      vi.mocked(repo.updateById).mockResolvedValue({ ...mockCustomer, licenseValidationStatus: 'APPROVED' } as any)
      const result = await service.approveLicense('cust_1', 'comp_1', 'APPROVE', undefined, 'Admin User')
      expect(repo.updateById).toHaveBeenCalledWith('cust_1', expect.objectContaining({ licenseValidationStatus: 'APPROVED' }))
    })

    it('sets DENIED status', async () => {
      vi.mocked(repo.findByIdSimple).mockResolvedValue(mockCustomer as any)
      vi.mocked(repo.updateById).mockResolvedValue({ ...mockCustomer, licenseValidationStatus: 'DENIED' } as any)
      await service.approveLicense('cust_1', 'comp_1', 'DENY', 'Expired document', 'Admin User')
      expect(repo.updateById).toHaveBeenCalledWith('cust_1', expect.objectContaining({ licenseValidationStatus: 'DENIED' }))
    })
  })
})
