import { amazonFeeCode, amazonMarketplaceCode } from '~/utils/amazonFeeCodes'
import { formatLexofficeDate } from '~/utils/lexofficeExport'

// ONE classifier for Amazon settlement rows, used by BOTH the settlement grid
// (Kürzel / Klassifikation columns) and the lexoffice CSV builder, so what the
// bookkeeper sees in the table is exactly what ends up in the Verwendungszweck.
// Buckets and codes follow PayJoe's Amazon layout:
//   Bestellung      <AmazonOrderId>                 (gross sale, matches the invoice)
//   Erstattung      <AmazonOrderId>                 (refund, matches the credit memo)
//   Bestellgebühr   AMA-BG-<MP>-<AmazonOrderId>     (commission / FBA fee of an order)
//   Servicegebühr   AMA-SG-<MP>-<Kürzel>            (subscription, return postage, RefundCommission, …)
//   Werbekosten     AMA-SG-<MP>-CoAdv               (one row per Ads invoice)
//   Auszahlung      AUSZ-<MP>-<dd.mm.yyyy>          (payout, negative)
//   Sonstiges       AMA-SG-<MP>-<Kürzel>            (reserve movements, unknown events)

export type AmazonRowBucket = 'sale' | 'refund' | 'orderFee' | 'serviceFee' | 'advertising' | 'other' | 'payout'

export const AMAZON_BUCKET_LABELS: Record<AmazonRowBucket, string> = {
  sale: 'Bestellung',
  refund: 'Erstattung',
  orderFee: 'Bestellgebühr',
  serviceFee: 'Servicegebühr',
  advertising: 'Werbekosten',
  other: 'Sonstiges',
  payout: 'Auszahlung'
}

// Zusatzinfo column: the action / tax treatment a row needs in lexoffice.
// Fees are invoiced by Amazon EU S.à r.l. (LU) → Reverse Charge §13b UStG;
// Amazon Ads invoices for DE sellers carry German VAT (PayJoe: 4600 with
// tax key 9 = 19 % Vorsteuer) → attach the Ads invoice instead.
export const AMAZON_BUCKET_HINTS: Record<AmazonRowBucket, string> = {
  sale: 'Rechnung zuordnen',
  refund: 'Gutschrift zuordnen',
  orderFee: 'Provision · RC §13b',
  serviceFee: 'Gebühr · RC §13b',
  advertising: 'Werbung · Ads-Rechnung',
  other: 'Prüfen',
  payout: 'Geldtransit'
}

export interface AmazonSettlementRowLike {
  category: string
  transactionType?: string
  amountType?: string
  amountDescription?: string
  amount: number
  orderId?: string
  merchantOrderId?: string
  postedDate?: string
  marketplace?: string
  internalDocumentNo?: string
  invoiceDocumentNo?: string
  bucket?: AmazonRowBucket
}

const SERVICE_FEE_TYPES_WITH_ORDER = new Set(['RefundCommission'])

export const classifyAmazonRow = (row: AmazonSettlementRowLike): AmazonRowBucket => {
  if (row.bucket === 'payout') return 'payout'
  const hasOrder = !!row.orderId
  switch (row.category) {
    case 'umsaetze':
      return 'sale'
    case 'retouren':
      // ReturnPostageBilling adjustments carry no order id → PayJoe's AMA-SG-…-RetPosBi
      return hasOrder ? 'refund' : 'serviceFee'
    case 'amazonFees':
    case 'fbaFees':
      if (!hasOrder) return 'serviceFee'
      if (row.transactionType === 'ServiceFee') return 'serviceFee'
      if (SERVICE_FEE_TYPES_WITH_ORDER.has(String(row.amountDescription || ''))) return 'serviceFee'
      return 'orderFee'
    case 'werbekosten':
      return 'advertising'
    default:
      return 'other'
  }
}

// The fee-type name a Servicegebühr / Sonstiges row is coded by.
const feeNameOf = (row: AmazonSettlementRowLike, bucket: AmazonRowBucket) => {
  if (bucket === 'advertising') return 'Cost of Advertising'
  return row.amountDescription || row.amountType || row.transactionType || 'Other'
}

// Most frequent marketplace among rows that carry one ("Amazon.de" → "DE");
// used as the fallback for rows without a marketplace (fees, ads, adjustments).
export const dominantAmazonMarketplaceCode = (rows: AmazonSettlementRowLike[], fallback = 'DE'): string => {
  const counts = new Map<string, number>()
  for (const r of rows || []) {
    if (!r.marketplace) continue
    const code = amazonMarketplaceCode(r.marketplace, '')
    if (!code) continue
    counts.set(code, (counts.get(code) || 0) + 1)
  }
  let best = ''
  let bestN = 0
  for (const [code, n] of counts) {
    if (n > bestN) { best = code; bestN = n }
  }
  return best || fallback
}

// PayJoe "Abkürzung" — the leading token of the Verwendungszweck.
export const amazonRowCode = (row: AmazonSettlementRowLike, marketplaceFallback = 'DE'): string => {
  const bucket = classifyAmazonRow(row)
  const mp = amazonMarketplaceCode(row.marketplace || '', marketplaceFallback)
  switch (bucket) {
    case 'sale':
    case 'refund':
      return row.orderId || ''
    case 'orderFee':
      return `AMA-BG-${mp}-${row.orderId}`
    case 'payout':
      return `AUSZ-${mp}-${formatLexofficeDate(row.postedDate)}`
    default:
      return `AMA-SG-${mp}-${amazonFeeCode(feeNameOf(row, bucket))}`
  }
}

export const amazonBucketLabel = (bucket: AmazonRowBucket) => AMAZON_BUCKET_LABELS[bucket] || bucket
export const amazonBucketHint = (bucket: AmazonRowBucket) => AMAZON_BUCKET_HINTS[bucket] || 'Prüfen'

const isoDay = (value?: string) => {
  if (!value) return ''
  const d = new Date(value)
  return isNaN(d.getTime()) ? '' : d.toISOString().split('T')[0]
}

const earlier = (a?: string, b?: string) => {
  if (!a) return b || ''
  if (!b) return a
  return new Date(a).getTime() <= new Date(b).getTime() ? a : b
}

export interface AmazonLexofficeGroup {
  bucket: AmazonRowBucket
  code: string
  orderId: string
  reference: string // ads invoice id / adjustment reference
  feeTypes: Set<string>
  description: string
  amount: number
  postedDate: string
  internalDocumentNo: string
  invoiceDocumentNo: string
}

// Collapses detail rows into export lines the way PayJoe does: one gross line
// per order, one fee line per order, one line per service fee / Ads invoice /
// adjustment (keyed by code + reference + day).
export const groupAmazonRowsForLexoffice = (rows: AmazonSettlementRowLike[], marketplaceFallback = 'DE'): AmazonLexofficeGroup[] => {
  const groups = new Map<string, AmazonLexofficeGroup>()
  for (const r of rows || []) {
    const bucket = classifyAmazonRow(r)
    if (bucket === 'payout') continue
    const code = amazonRowCode(r, marketplaceFallback)
    const day = isoDay(r.postedDate)
    let key: string
    switch (bucket) {
      case 'sale':
      case 'refund':
      case 'orderFee':
        key = `${bucket}|${r.orderId}`
        break
      case 'advertising':
        key = `${bucket}|${r.merchantOrderId || ''}|${day}`
        break
      default:
        key = `${bucket}|${code}|${r.orderId || ''}|${r.merchantOrderId || ''}|${day}`
    }
    const g = groups.get(key) || {
      bucket,
      code,
      orderId: r.orderId || '',
      reference: r.merchantOrderId || '',
      feeTypes: new Set<string>(),
      description: r.amountDescription || r.amountType || r.transactionType || '',
      amount: 0,
      postedDate: '',
      internalDocumentNo: r.internalDocumentNo || '',
      invoiceDocumentNo: r.invoiceDocumentNo || ''
    }
    g.amount += Number(r.amount) || 0
    g.postedDate = earlier(g.postedDate, r.postedDate)
    if (r.amountDescription) g.feeTypes.add(r.amountDescription)
    if (!g.internalDocumentNo && r.internalDocumentNo) g.internalDocumentNo = r.internalDocumentNo
    if (!g.invoiceDocumentNo && r.invoiceDocumentNo) g.invoiceDocumentNo = r.invoiceDocumentNo
    groups.set(key, g)
  }
  return [...groups.values()].filter(g => Math.round(g.amount * 100) !== 0)
}

// Verwendungszweck for one export group. Sales carry the internal invoice
// number (what lexoffice's invoice matching keys on); fee lines only reference
// it for traceability.
export const amazonGroupPurpose = (g: AmazonLexofficeGroup): string => {
  const ref = g.invoiceDocumentNo
    ? ` · Rechnung ${g.invoiceDocumentNo}`
    : g.internalDocumentNo
      ? ` · Auftrag ${g.internalDocumentNo}`
      : ' · nicht zugeordnet'
  const fees = [...g.feeTypes].filter(Boolean).join(', ')
  switch (g.bucket) {
    case 'sale':
      return `${g.orderId} · Amazon Verkauf${ref}`
    case 'refund':
      return `${g.orderId} · Amazon Erstattung${ref}`
    case 'orderFee':
      return `${g.code} · Amazon Bestellgebühr${fees ? ` (${fees})` : ''}${g.invoiceDocumentNo ? ` · zu Rechnung ${g.invoiceDocumentNo}` : g.internalDocumentNo ? ` · zu Auftrag ${g.internalDocumentNo}` : ''}`
    case 'advertising':
      return `${g.code} · Amazon Werbekosten${g.reference ? ` · Rechnung ${g.reference}` : ''}`
    case 'serviceFee':
      return `${g.code} · Amazon Servicegebühr ${g.description}${g.orderId ? ` · ${g.orderId}` : ''}${g.reference ? ` · ${g.reference}` : ''}`
    default:
      return `${g.code} · Amazon ${g.description || 'Sonstiges'}${g.orderId ? ` · ${g.orderId}` : ''}${g.reference ? ` · ${g.reference}` : ''}`
  }
}
