import {
  applyVat as platformApplyVat,
  extractVat as platformExtractVat,
  fromFraction,
} from '@platform-modules/tax/rates-table'

const AMOUNT_PATTERN = /^(?:0|[1-9]\d*)(?:\.\d{1,2})?$/
const RATE_PATTERN = /^(?:0|[1-9]\d*)(?:\.\d{1,4})?$/

export { applyVat, extractVat } from '@platform-modules/tax/rates-table'

export function roundHalfUp(numerator: bigint, denominator: bigint): bigint {
  if (denominator <= 0n) {
    throw new Error(`denominator must be positive, got ${denominator}`)
  }
  if (numerator < 0n) {
    throw new Error(`numerator must be non-negative, got ${numerator}`)
  }
  return (2n * numerator + denominator) / (2n * denominator)
}

function normalizeAmount(amount: string | number): string {
  const normalized = typeof amount === 'number' ? amount.toFixed(2) : amount
  if (!AMOUNT_PATTERN.test(normalized)) {
    throw new Error(`amount must be a non-negative decimal with up to 2 digits, got ${JSON.stringify(amount)}`)
  }
  const [whole = '0', fraction = ''] = normalized.split('.')
  return `${whole}.${fraction.padEnd(2, '0')}`
}

function normalizeVatRate(vatRate: string | number | null | undefined): string {
  if (vatRate == null) return '0.0000'
  const normalized = typeof vatRate === 'number' ? vatRate.toFixed(4) : vatRate
  if (!RATE_PATTERN.test(normalized)) {
    throw new Error(`vatRate must be a non-negative decimal with up to 4 digits, got ${JSON.stringify(vatRate)}`)
  }
  const [whole = '0', fraction = ''] = normalized.split('.')
  return `${whole}.${fraction.padEnd(4, '0')}`
}

export function decimalAmountToAgorot(amount: string | number): bigint {
  const normalized = normalizeAmount(amount)
  const [whole = '0', fraction = '00'] = normalized.split('.')
  return BigInt(whole) * 100n + BigInt(fraction)
}

export function agorotToDecimalAmount(amount: bigint): string {
  if (amount < 0n) {
    throw new Error(`amount must be non-negative agorot, got ${amount}`)
  }
  const whole = amount / 100n
  const fraction = (amount % 100n).toString().padStart(2, '0')
  return `${whole}.${fraction}`
}

export function decimalRateToBasisPoints(vatRate: string | number | null | undefined) {
  return fromFraction(normalizeVatRate(vatRate))
}

function normalizedRateToBasisPoints(vatRate: string): bigint {
  const [whole = '0', fraction = '0000'] = vatRate.split('.')
  return BigInt(whole) * 10_000n + BigInt(fraction)
}

export function calculateHostInvoiceVat(
  netAmount: string | number,
  vatRate: string | number | null | undefined,
) {
  const normalizedNet = normalizeAmount(netAmount)
  const normalizedVatRate = normalizeVatRate(vatRate)
  const netAgorot = decimalAmountToAgorot(normalizedNet)
  const rateBasisPoints = normalizedRateToBasisPoints(normalizedVatRate)
  const vatAgorot = roundHalfUp(netAgorot * rateBasisPoints, 10_000n)
  const grossAgorot = netAgorot + vatAgorot

  return {
    net: normalizedNet,
    vat: agorotToDecimalAmount(vatAgorot),
    gross: agorotToDecimalAmount(grossAgorot),
    vatRate: normalizedVatRate,
  }
}

export function applyInvoiceVat(
  netAmount: string | number,
  vatRate: string | number | null | undefined,
) {
  const normalizedNet = normalizeAmount(netAmount)
  const normalizedVatRate = normalizeVatRate(vatRate)
  const { vat, gross } = platformApplyVat(
    decimalAmountToAgorot(normalizedNet),
    decimalRateToBasisPoints(normalizedVatRate),
  )

  return {
    net: normalizedNet,
    vat: agorotToDecimalAmount(vat),
    gross: agorotToDecimalAmount(gross),
    vatRate: normalizedVatRate,
  }
}

export function extractInvoiceVat(
  grossAmount: string | number,
  vatRate: string | number | null | undefined,
) {
  const normalizedGross = normalizeAmount(grossAmount)
  const normalizedVatRate = normalizeVatRate(vatRate)
  const { net, vat } = platformExtractVat(
    decimalAmountToAgorot(normalizedGross),
    decimalRateToBasisPoints(normalizedVatRate),
  )

  return {
    net: agorotToDecimalAmount(net),
    vat: agorotToDecimalAmount(vat),
    gross: normalizedGross,
    vatRate: normalizedVatRate,
  }
}
