import { uploadImage } from '../../lib/storage'
import { ConflictError, ForbiddenError, NotFoundError } from '../../http/errors'
import { presentCompany, presentBrand } from './company.presenter'
import * as repo from './company.repo'
import { resolveSettingsEntitlements, SettingsFeatureKey } from './settingsEntitlements'

function buildPaymentMethodsEnabled(input: {
  amanpayMerchantId?: string | null
  amanpaySecretKey?: string | null
  paypalEmail?: string | null
}) {
  const methods: Array<'AMANPAY' | 'PAYPAL'> = []
  if (input.amanpayMerchantId && input.amanpaySecretKey) methods.push('AMANPAY')
  if (input.paypalEmail) methods.push('PAYPAL')
  return methods
}

export async function getCompany(companyId: string) {
  return presentCompany(await repo.findCompany(companyId))
}

export async function updateCompany(companyId: string, data: any) {
  return presentCompany(await repo.updateCompany(companyId, data))
}

export async function getBrand(companyId: string) {
  return presentBrand(await repo.findBrand(companyId))
}

export async function updateBrand(companyId: string, body: any, companyName: string, companySlug: string) {
  await assertSettingsFeature(companyId, 'settings.branding_basic')
  if (body.primaryColor || body.accentColor) await assertSettingsFeature(companyId, 'settings.branding_custom')
  if (body.amanpayMerchantId || body.amanpaySecretKey || body.paypalEmail || body.paypalMerchantId) {
    await assertSettingsFeature(companyId, 'settings.renter_payments')
  }

  const current = await repo.findBrand(companyId)
  const paymentMethodsEnabled = buildPaymentMethodsEnabled({
    amanpayMerchantId: body.amanpayMerchantId ?? current?.amanpayMerchantId,
    amanpaySecretKey:  body.amanpaySecretKey  ?? current?.amanpaySecretKey,
    paypalEmail:       body.paypalEmail       ?? current?.paypalEmail,
  })
  return presentBrand(await repo.upsertBrand(
    companyId,
    { ...body, paymentMethodsEnabled },
    { displayName: body.displayName ?? companyName, subdomain: companySlug, paymentMethodsEnabled, ...body },
  ))
}

export async function uploadLogo(companyId: string, companyName: string, companySlug: string, file: Buffer) {
  await assertSettingsFeature(companyId, 'settings.branding_basic')
  const url = await uploadImage(file, `companies/${companyId}/brand`, 'logo')
  return presentBrand(await repo.upsertBrand(
    companyId,
    { logoUrl: url },
    { displayName: companyName, subdomain: companySlug, logoUrl: url },
  ))
}

export async function uploadHeroImage(companyId: string, companyName: string, companySlug: string, file: Buffer) {
  await assertSettingsFeature(companyId, 'settings.branding_hero')
  const url = await uploadImage(file, `companies/${companyId}/brand`, 'hero')
  return presentBrand(await repo.upsertBrand(
    companyId,
    { heroImageUrl: url },
    { displayName: companyName, subdomain: companySlug, heroImageUrl: url },
  ))
}

export async function checkSubdomainAvailability(subdomain: string, companyId: string) {
  const existing = await repo.findBrandBySubdomain(subdomain, companyId)
  return { available: !existing }
}

export async function setCustomDomain(companyId: string, companyName: string, companySlug: string, customDomain: string) {
  const normalized = customDomain.toLowerCase().trim()
  const existing = await repo.findBrandByCustomDomain(normalized, companyId)
  if (existing) throw new ConflictError('This custom domain is already in use')
  return repo.upsertBrand(
    companyId,
    { customDomain: normalized, customDomainVerified: false, customDomainAddedAt: new Date() },
    { displayName: companyName, subdomain: companySlug, customDomain: normalized, customDomainVerified: false, customDomainAddedAt: new Date() },
  )
}

export async function getCustomDomainStatus(companyId: string) {
  const brand = await repo.findBrand(companyId)
  const customDomain = brand?.customDomain ?? null
  return {
    customDomain,
    verified: brand?.customDomainVerified ?? false,
    status: customDomain ? (brand?.customDomainVerified ? 'verified' : 'pending_dns') : 'not_configured',
    dnsTarget: process.env.NEXT_PUBLIC_CUSTOM_DOMAIN_TARGET ?? 'cname.vercel-dns.com',
  }
}

export async function removeCustomDomain(companyId: string) {
  const result = await repo.clearCustomDomain(companyId)
  return { success: result.count > 0 }
}

export async function getContractSettings(companyId: string) {
  return repo.findContractSettings(companyId)
}

export async function updateContractSettings(companyId: string, data: any) {
  await assertSettingsFeature(companyId, 'settings.rental_policies_basic')
  if (
    data.additionalDriverCharge ||
    data.additionalDriverDailyRate !== undefined ||
    data.additionalDriverFlatRate !== undefined
  ) {
    await assertSettingsFeature(companyId, 'settings.additional_driver_fees')
  }
  return repo.upsertContractSettings(companyId, data)
}

export async function getInsurancePolicies(companyId: string) {
  return repo.findInsurancePolicies(companyId)
}

export async function createInsurancePolicy(companyId: string, data: any) {
  await assertSettingsFeature(companyId, 'settings.insurance_policies')
  return repo.createInsurancePolicy(companyId, data)
}

export async function updateInsurancePolicy(id: string, companyId: string, data: any) {
  await assertSettingsFeature(companyId, 'settings.insurance_policies')
  const result = await repo.updateInsurancePolicy(id, companyId, data)
  if (result.count === 0) throw new NotFoundError('Insurance policy not found')
  return repo.findInsurancePolicyOrThrow(id)
}

export async function deleteInsurancePolicy(id: string, companyId: string) {
  await assertSettingsFeature(companyId, 'settings.insurance_policies')
  const result = await repo.deleteInsurancePolicy(id, companyId)
  if (result.count === 0) throw new NotFoundError('Insurance policy not found')
}

export async function getPricingRules(companyId: string) {
  return repo.findPricingRules(companyId)
}

export async function createPricingRule(companyId: string, data: any) {
  await assertSettingsFeature(companyId, 'settings.pricing_rules')
  return repo.createPricingRule(companyId, data)
}

export async function updatePricingRule(id: string, companyId: string, data: any) {
  await assertSettingsFeature(companyId, 'settings.pricing_rules')
  const result = await repo.updatePricingRule(id, companyId, data)
  if (result.count === 0) throw new NotFoundError('Pricing rule not found')
  return repo.findPricingRuleOrThrow(id)
}

export async function deletePricingRule(id: string, companyId: string) {
  await assertSettingsFeature(companyId, 'settings.pricing_rules')
  const result = await repo.deletePricingRule(id, companyId)
  if (result.count === 0) throw new NotFoundError('Pricing rule not found')
}

export async function getAccountingSettings(companyId: string) {
  return repo.findAccountingSettings(companyId)
}

export async function updateAccountingSettings(companyId: string, data: any) {
  await assertSettingsFeature(companyId, 'settings.accounting_defaults')
  if (data.autoSendReport) await assertSettingsFeature(companyId, 'settings.accounting_scheduled_delivery')
  if (data.reportFormat === 'BOTH') await assertSettingsFeature(companyId, 'settings.accounting_advanced_formats')
  return repo.upsertAccountingSettings(companyId, data)
}

export async function getApiKey(companyId: string) {
  return repo.findApiKey(companyId)
}

export async function regenerateApiKey(companyId: string) {
  return repo.regenerateApiKey(companyId)
}

async function assertSettingsFeature(companyId: string, featureKey: SettingsFeatureKey) {
  const entitlements = await resolveSettingsEntitlements(companyId)
  if (!entitlements.features[featureKey]?.editable) {
    throw new ForbiddenError('This settings feature is not available for your current subscription.')
  }
}
