import { downloadLexofficeCsv, type LexofficeCsvRow } from '~/utils/lexofficeExport'

// Shared export orchestration for the marketplace settlement pages (Amazon
// settlement periods / eBay payouts) → lexoffice bank-statement CSV.
//
// Three variants, all driven by the SAME list of ticked periods:
//   - combined  : positions + the closing payout ("Geldtransit") row per period
//                 in ONE file — PayJoe's "Sammelkonto" layout, every period nets
//                 to exactly 0,00 on the clearing account. Recommended default.
//   - payouts   : only the payout rows (one per period). Pure list data.
//   - positions : only the position rows (per order / fee / ad invoice).
// The page supplies the marketplace-specific pieces (how to fetch a period's
// detail rows and how to turn them into CSV rows); everything else — progress,
// warnings, foreign-currency skips, netting check, sorting, file naming — lives
// here once instead of twice.

export interface LexofficeRowsResult {
  rows: LexofficeCsvRow[]
  warning?: string
}

export interface LexofficeSettlementExportOptions<TItem> {
  // Loads the detail rows + summary of one period. Return { error } to skip it.
  fetchDetail: (item: TItem) => Promise<{ rows?: any[]; summary?: any; error?: string }>
  buildPositionRows: (item: TItem, detailRows: any[], summary: any) => LexofficeRowsResult
  buildPayoutRow: (item: TItem) => LexofficeRowsResult
  // Short human label for warnings ("Zeitraum 01.06.–15.06.2026").
  itemLabel: (item: TItem) => string
  // ISO date used for the file name range (payout date).
  itemDate: (item: TItem) => string | null | undefined
  filePrefix: string // e.g. 'lexoffice-amazon'
  onAlert: (alert: { status: number; message: string }) => void
  emptySelectionMessage: () => string
  // Optional: called AFTER the CSV file was handed to the browser, with the
  // detail rows of every period that went into it (only for variants that
  // contain positions — payouts-only exports carry no order data). Used by
  // the Amazon page to mark the exported orders as "CSV-exportiert".
  onExported?: (variant: LexofficeExportVariant, exported: { item: TItem; rows: any[] }[]) => void | Promise<void>
}

export type LexofficeExportVariant = 'combined' | 'payouts' | 'positions'

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

const rowTime = (row: LexofficeCsvRow) => {
  const d = row.bookingDate instanceof Date ? row.bookingDate : new Date(row.bookingDate || '')
  return isNaN(d.getTime()) ? Number.MAX_SAFE_INTEGER : d.getTime()
}

const round2 = (n: number) => Math.round((Number(n) || 0) * 100) / 100

export const useLexofficeSettlementExport = <TItem>(options: LexofficeSettlementExportOptions<TItem>) => {
  const exporting = ref(false)
  const progress = ref({ done: 0, total: 0 })

  const buildFileName = (variant: LexofficeExportVariant, items: TItem[]) => {
    const days = items.map(i => isoDay(options.itemDate(i))).filter(Boolean).sort()
    const range = days.length ? `${days[0]}_${days[days.length - 1]}` : new Date().toISOString().split('T')[0]
    const part = variant === 'combined' ? 'sammelkonto' : variant === 'payouts' ? 'auszahlungen' : 'positionen'
    return `${options.filePrefix}-${part}-${range}.csv`
  }

  const finish = (variant: LexofficeExportVariant, items: TItem[], allRows: LexofficeCsvRow[], warnings: string[]): boolean => {
    if (!allRows.length) {
      options.onAlert({ status: 400, message: 'Keine exportierbaren Zeilen gefunden.' + (warnings.length ? ' — ' + warnings.join(' | ') : '') })
      return false
    }
    // Stable chronological order (Buchungstag), payout row of a period last
    // among equal dates because it is pushed after the positions.
    const sorted = allRows
      .map((row, idx) => ({ row, idx, t: rowTime(row) }))
      .sort((a, b) => a.t - b.t || a.idx - b.idx)
      .map(x => x.row)
    const result = downloadLexofficeCsv(sorted, buildFileName(variant, items))
    const base = variant === 'combined'
      ? `Lexoffice-CSV erstellt: ${result.rowCount} Zeilen für ${items.length} Zeitraum/Zeiträume (Positionen + Auszahlung, je Zeitraum Saldo 0,00). In lexoffice auf das Verrechnungskonto importieren; die Auszahlungszeile als Geldtransit mit dem Bankeingang verknüpfen.`
      : variant === 'payouts'
        ? `Auszahlungs-CSV erstellt: ${result.rowCount} Zeilen. Jede Zeile in lexoffice als Geldtransit kategorisieren und mit dem passenden Eingang auf dem Bankkonto verknüpfen.`
        : `Positions-CSV erstellt: ${result.rowCount} Zeilen.`
    options.onAlert({
      status: 200,
      message: warnings.length ? `${base} Warnungen: ${warnings.join(' | ')}` : base
    })
    return true
  }

  const run = async (variant: LexofficeExportVariant, items: TItem[]) => {
    if (exporting.value) return
    if (!items.length) {
      options.onAlert({ status: 400, message: options.emptySelectionMessage() })
      return
    }
    const needsDetail = variant !== 'payouts'
    exporting.value = true
    progress.value = { done: 0, total: needsDetail ? items.length : 0 }
    const allRows: LexofficeCsvRow[] = []
    const warnings: string[] = []
    // Periods whose position rows actually made it into the file (for onExported).
    const exportedDetails: { item: TItem; rows: any[] }[] = []
    try {
      for (const item of items) {
        const label = options.itemLabel(item)
        const periodRows: LexofficeCsvRow[] = []
        try {
          if (needsDetail) {
            const detail = await options.fetchDetail(item)
            if (detail?.error) {
              warnings.push(`${label}: ${detail.error}`)
              continue
            }
            const pos = options.buildPositionRows(item, detail?.rows || [], detail?.summary)
            if (pos.warning) warnings.push(pos.warning)
            periodRows.push(...pos.rows)
            if (pos.rows.length) exportedDetails.push({ item, rows: detail?.rows || [] })
          }
          if (variant !== 'positions') {
            const pay = options.buildPayoutRow(item)
            if (pay.warning && !needsDetail) warnings.push(pay.warning)
            periodRows.push(...pay.rows)
          }
          if (variant === 'combined' && periodRows.length) {
            // Every period must net to 0,00 on the clearing account. The position
            // builder already surfaces any reconciliation residue as its own row,
            // so a non-zero here means a genuine data problem worth flagging.
            const net = round2(periodRows.reduce((s, r) => s + round2(r.amount), 0))
            if (Math.abs(net) >= 0.01) {
              warnings.push(`${label}: Saldo ${net.toFixed(2).replace('.', ',')} ≠ 0,00 — bitte prüfen`)
            }
          }
          allRows.push(...periodRows)
        } catch (error: any) {
          warnings.push(`${label}: ${error?.data?.message || error?.message || 'Fehler beim Laden'}`)
        } finally {
          if (needsDetail) progress.value.done++
        }
      }
    } finally {
      exporting.value = false
    }
    const downloaded = finish(variant, items, allRows, warnings)
    if (downloaded && needsDetail && exportedDetails.length && options.onExported) {
      await options.onExported(variant, exportedDetails)
    }
  }

  return {
    exporting,
    progress,
    exportCombined: (items: TItem[]) => run('combined', items),
    exportPayoutsOnly: (items: TItem[]) => run('payouts', items),
    exportPositionsOnly: (items: TItem[]) => run('positions', items)
  }
}
