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

vi.mock('../../lib/storage', () => ({ uploadImage: vi.fn().mockResolvedValue('/storage/companies/company_1/brand/logo.jpg') }))
vi.mock('./settingsEntitlements', () => ({
  resolveSettingsEntitlements: vi.fn(),
}))
vi.mock('./company.repo', () => ({
  findCompany: vi.fn(),
  updateCompany: vi.fn(),
  findBrand: vi.fn(),
  upsertBrand: vi.fn(),
  findBrandBySubdomain: vi.fn(),
  findBrandByCustomDomain: vi.fn(),
  clearCustomDomain: vi.fn(),
  findContractSettings: vi.fn(),
  upsertContractSettings: vi.fn(),
  findInsurancePolicies: vi.fn(),
  createInsurancePolicy: vi.fn(),
  updateInsurancePolicy: vi.fn(),
  findInsurancePolicyOrThrow: vi.fn(),
  deleteInsurancePolicy: vi.fn(),
  findPricingRules: vi.fn(),
  createPricingRule: vi.fn(),
  updatePricingRule: vi.fn(),
  findPricingRuleOrThrow: vi.fn(),
  deletePricingRule: vi.fn(),
  findAccountingSettings: vi.fn(),
  upsertAccountingSettings: vi.fn(),
  findApiKey: vi.fn(),
  regenerateApiKey: vi.fn(),
}))

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 currentBrand = {
  id: 'brand_1',
  companyId: 'company_1',
  displayName: 'Atlas Cars',
  subdomain: 'atlas',
  amanpayMerchantId: 'merchant_1',
  amanpaySecretKey: 'secret_1',
  paypalEmail: null,
  paypalMerchantId: null,
  paymentMethodsEnabled: ['AMANPAY'],
}

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

describe('company.service edge behavior', () => {
  it('preserves existing AmanPay credentials when recomputing enabled payment methods', async () => {
    vi.mocked(repo.findBrand).mockResolvedValue(currentBrand as any)
    vi.mocked(repo.upsertBrand).mockResolvedValue({ ...currentBrand, paypalEmail: 'billing@example.test' } as any)

    const result = await service.updateBrand('company_1', { paypalEmail: 'billing@example.test' }, 'Atlas Cars', 'atlas')

    expect(repo.upsertBrand).toHaveBeenCalledWith(
      'company_1',
      expect.objectContaining({
        paypalEmail: 'billing@example.test',
        paymentMethodsEnabled: ['AMANPAY', 'PAYPAL'],
      }),
      expect.objectContaining({
        displayName: 'Atlas Cars',
        subdomain: 'atlas',
        paypalEmail: 'billing@example.test',
        paymentMethodsEnabled: ['AMANPAY', 'PAYPAL'],
      }),
    )
    expect(result).not.toHaveProperty('paypalEmail')
    expect(result.paypalConfigured).toBe(true)
  })

  it('does not report AmanPay enabled when only one credential is available', async () => {
    vi.mocked(repo.findBrand).mockResolvedValue({ ...currentBrand, amanpaySecretKey: null } as any)
    vi.mocked(repo.upsertBrand).mockResolvedValue({ ...currentBrand, amanpaySecretKey: null } as any)

    await service.updateBrand('company_1', {}, 'Atlas Cars', 'atlas')

    expect(repo.upsertBrand).toHaveBeenCalledWith(
      'company_1',
      expect.objectContaining({ paymentMethodsEnabled: [] }),
      expect.objectContaining({ paymentMethodsEnabled: [] }),
    )
  })

  it('normalizes custom domains and marks them pending verification', async () => {
    vi.mocked(repo.findBrandByCustomDomain).mockResolvedValue(null as any)
    vi.mocked(repo.upsertBrand).mockResolvedValue({ id: 'brand_1', customDomain: 'cars.example.com' } as any)

    await service.setCustomDomain('company_1', 'Atlas Cars', 'atlas', '  CARS.Example.COM  ')

    expect(repo.findBrandByCustomDomain).toHaveBeenCalledWith('cars.example.com', 'company_1')
    expect(repo.upsertBrand).toHaveBeenCalledWith(
      'company_1',
      expect.objectContaining({ customDomain: 'cars.example.com', customDomainVerified: false, customDomainAddedAt: expect.any(Date) }),
      expect.objectContaining({ displayName: 'Atlas Cars', subdomain: 'atlas', customDomain: 'cars.example.com', customDomainVerified: false, customDomainAddedAt: expect.any(Date) }),
    )
  })

  it('rejects custom domains already owned by another company', async () => {
    vi.mocked(repo.findBrandByCustomDomain).mockResolvedValue({ id: 'other_brand' } as any)

    await expect(service.setCustomDomain('company_1', 'Atlas Cars', 'atlas', 'cars.example.com')).rejects.toMatchObject({ statusCode: 409 })
    expect(repo.upsertBrand).not.toHaveBeenCalled()
  })

  it('returns deterministic custom-domain status and honors configured DNS target', async () => {
    process.env.NEXT_PUBLIC_CUSTOM_DOMAIN_TARGET = 'tenant-target.example.net'
    vi.mocked(repo.findBrand).mockResolvedValue({ customDomain: 'cars.example.com', customDomainVerified: false } as any)

    await expect(service.getCustomDomainStatus('company_1')).resolves.toEqual({
      customDomain: 'cars.example.com',
      verified: false,
      status: 'pending_dns',
      dnsTarget: 'tenant-target.example.net',
    })
  })

  it('raises not found when updating a missing insurance policy', async () => {
    vi.mocked(repo.updateInsurancePolicy).mockResolvedValue({ count: 0 } as any)

    await expect(service.updateInsurancePolicy('policy_1', 'company_1', { name: 'CDW' })).rejects.toMatchObject({ statusCode: 404 })
    expect(repo.findInsurancePolicyOrThrow).not.toHaveBeenCalled()
  })

  it('raises not found when deleting a missing pricing rule', async () => {
    vi.mocked(repo.deletePricingRule).mockResolvedValue({ count: 0 } as any)

    await expect(service.deletePricingRule('rule_1', 'company_1')).rejects.toMatchObject({ statusCode: 404 })
  })
})
