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

vi.mock('./company.repo')
vi.mock('./settingsEntitlements', () => ({
  resolveSettingsEntitlements: vi.fn(),
}))
vi.mock('../../lib/storage', () => ({
  uploadImage: vi.fn().mockResolvedValue('http://localhost:4000/storage/companies/comp_1/brand/logo.jpg'),
}))

const repo = await import('./company.repo')
const entitlements = await import('./settingsEntitlements')
const service = await import('./company.service')

const editableSettingsFeatures = [
  'settings.company_profile',
  'settings.public_contact',
  'settings.locale_currency',
  'settings.carplace_listing',
  'settings.branding_basic',
  'settings.branding_custom',
  'settings.branding_hero',
  'settings.renter_payments',
  'settings.rental_policies_basic',
  'settings.additional_driver_fees',
  'settings.insurance_policies',
  'settings.pricing_rules',
  'settings.accounting_defaults',
  'settings.accounting_scheduled_delivery',
  'settings.accounting_advanced_formats',
] as const

function buildEditableEntitlements() {
  return {
    plan: 'PRO',
    subscriptionStatus: 'ACTIVE',
    currentPeriodEnd: null,
    trialEndAt: null,
    accessLevel: 'full',
    features: Object.fromEntries(editableSettingsFeatures.map((featureKey) => [featureKey, {
      available: true,
      editable: true,
      requiredPlan: null,
      reason: null,
    }])),
  }
}

const mockCompany = {
  id: 'comp_1',
  name: 'Test Rentals',
  slug: 'test-rentals',
  email: 'info@test.com',
  brand: null,
  subscription: null,
  _count: { vehicles: 5, customers: 10, reservations: 20 },
}

const mockBrand = {
  id: 'brand_1',
  companyId: 'comp_1',
  displayName: 'Test Rentals',
  subdomain: 'test-rentals',
  logoUrl: null,
  heroImageUrl: null,
  customDomain: null,
  customDomainVerified: false,
  amanpayMerchantId: null,
  amanpaySecretKey: null,
  paypalEmail: null,
}

beforeEach(() => {
  vi.clearAllMocks()
  vi.mocked(entitlements.resolveSettingsEntitlements).mockResolvedValue(buildEditableEntitlements() as any)
})

describe('company.service', () => {
  describe('getCompany', () => {
    it('returns company with brand and subscription', async () => {
      vi.mocked(repo.findCompany).mockResolvedValue(mockCompany as any)
      const result = await service.getCompany('comp_1')
      expect(result).toEqual(mockCompany)
      expect(repo.findCompany).toHaveBeenCalledWith('comp_1')
    })
  })

  describe('updateCompany', () => {
    it('updates company profile', async () => {
      vi.mocked(repo.updateCompany).mockResolvedValue({ ...mockCompany, name: 'Updated Rentals' } as any)
      const result = await service.updateCompany('comp_1', { name: 'Updated Rentals' })
      expect(repo.updateCompany).toHaveBeenCalledWith('comp_1', { name: 'Updated Rentals' })
    })
  })

  describe('uploadLogo', () => {
    it('uploads logo and upserts brand', async () => {
      vi.mocked(repo.upsertBrand).mockResolvedValue({ ...mockBrand, logoUrl: 'http://localhost:4000/storage/companies/comp_1/brand/logo.jpg' } as any)
      const result = await service.uploadLogo('comp_1', 'Test Rentals', 'test-rentals', Buffer.from(''))
      expect(repo.upsertBrand).toHaveBeenCalledWith(
        'comp_1',
        expect.objectContaining({ logoUrl: 'http://localhost:4000/storage/companies/comp_1/brand/logo.jpg' }),
        expect.objectContaining({ logoUrl: 'http://localhost:4000/storage/companies/comp_1/brand/logo.jpg' }),
      )
    })
  })

  describe('uploadHeroImage', () => {
    it('uploads hero image and upserts brand', async () => {
      vi.mocked(repo.upsertBrand).mockResolvedValue({ ...mockBrand, heroImageUrl: 'http://localhost:4000/storage/companies/comp_1/brand/logo.jpg' } as any)
      await service.uploadHeroImage('comp_1', 'Test Rentals', 'test-rentals', Buffer.from(''))
      expect(repo.upsertBrand).toHaveBeenCalledWith(
        'comp_1',
        expect.objectContaining({ heroImageUrl: expect.any(String) }),
        expect.objectContaining({ heroImageUrl: expect.any(String) }),
      )
    })
  })

  describe('checkSubdomainAvailability', () => {
    it('returns available=true when subdomain is free', async () => {
      vi.mocked(repo.findBrandBySubdomain).mockResolvedValue(null)
      const result = await service.checkSubdomainAvailability('my-rental', 'comp_1')
      expect(result.available).toBe(true)
    })

    it('returns available=false when subdomain is taken', async () => {
      vi.mocked(repo.findBrandBySubdomain).mockResolvedValue(mockBrand as any)
      const result = await service.checkSubdomainAvailability('taken-rental', 'comp_1')
      expect(result.available).toBe(false)
    })
  })

  describe('setCustomDomain', () => {
    it('throws ConflictError when custom domain is already in use', async () => {
      vi.mocked(repo.findBrandByCustomDomain).mockResolvedValue(mockBrand as any)
      await expect(service.setCustomDomain('comp_1', 'Test Rentals', 'test-rentals', 'taken.com')).rejects.toThrow('This custom domain is already in use')
    })

    it('sets custom domain when available', async () => {
      vi.mocked(repo.findBrandByCustomDomain).mockResolvedValue(null)
      vi.mocked(repo.upsertBrand).mockResolvedValue({ ...mockBrand, customDomain: 'my-rental.com' } as any)
      await service.setCustomDomain('comp_1', 'Test Rentals', 'test-rentals', 'My-Rental.com')
      expect(repo.upsertBrand).toHaveBeenCalledWith(
        'comp_1',
        expect.objectContaining({ customDomain: 'my-rental.com' }),
        expect.objectContaining({ customDomain: 'my-rental.com' }),
      )
    })
  })

  describe('updateInsurancePolicy', () => {
    it('throws NotFoundError when policy does not belong to company', async () => {
      vi.mocked(repo.updateInsurancePolicy).mockResolvedValue({ count: 0 } as any)
      await expect(service.updateInsurancePolicy('pol_1', 'comp_1', { name: 'CDW' })).rejects.toThrow('Insurance policy not found')
    })
  })

  describe('deletePricingRule', () => {
    it('throws NotFoundError when rule does not belong to company', async () => {
      vi.mocked(repo.deletePricingRule).mockResolvedValue({ count: 0 } as any)
      await expect(service.deletePricingRule('rule_1', 'comp_1')).rejects.toThrow('Pricing rule not found')
    })
  })
})
