import { loadPdfMake } from '~/utils/loadPdfMake'

/**
 * Merchant-facing ticket documentation PDF (German).
 *
 * Purpose: merchants cannot see tickets that are org-assigned to LogYou — this
 * report is what support hands them on request. Contents:
 *   1. Header: general ticket information
 *   2. "Zeiterfassung & Kosten": ALL time-billing entries of the ticket grouped
 *      into one table (parsed from the [Zeitabrechnung] request-updates +
 *      QtySpent minutes), with totals reconciled against the ticket totals
 *   3. "Verlauf": every step/message chronologically, grouped by day
 *      (request-updates + request-history; the changelog stream is skipped on
 *      purpose — it is unreliable for R_Request and internal noise)
 *
 * Data is fetched here (request-update + request-history); resolved header
 * fields come from the edit page's form (identifiers already readable).
 */

interface ReportHeader {
  documentNo: string
  summary: string
  partner: string
  organization: string
  contact: string
  salesRep: string
  requestType: string
  category: string
  status: string
  resolution: string
  priority: string
  created: string
  dateLastAction: string
  startDate: string
  endDate: string
  qtySpentTotal: number
  requestAmtTotal: number
}

const parseGermanAmount = (raw: string): number | null => {
  const cleaned = String(raw).replace(/[^\d,.-]/g, '')
  if (!cleaned) return null
  const normalized = cleaned.replace(/\./g, '').replace(',', '.')
  const value = Number(normalized)
  return isNaN(value) ? null : value
}

const formatEur = (value: number | null | undefined): string => {
  if (value === null || value === undefined || isNaN(value)) return ''
  return Number(value).toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' €'
}

const formatMinutes = (minutes: number): string => {
  const m = Math.max(0, Math.round(minutes))
  const h = Math.floor(m / 60)
  const rest = m % 60
  return h > 0 ? `${h}h ${rest}min` : `${rest}min`
}

const formatDateTime = (val: any): string => {
  if (!val) return ''
  const d = new Date(val)
  if (isNaN(d.getTime())) return ''
  return d.toLocaleString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' })
}

const formatDay = (val: any): string => {
  if (!val) return ''
  const d = new Date(val)
  if (isNaN(d.getTime())) return ''
  return d.toLocaleDateString('de-DE', { weekday: 'long', day: '2-digit', month: '2-digit', year: 'numeric' })
}

const formatTime = (val: any): string => {
  if (!val) return ''
  const d = new Date(val)
  if (isNaN(d.getTime())) return ''
  return d.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })
}

// Rewrite ISO timestamps inside [START:...]/[END:...] markers into readable form
const humanizeMarkers = (text: string): string => {
  return String(text).replace(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:[+-]\d{2}:?\d{2}|Z)?/g, (iso) => {
    const d = new Date(iso)
    return isNaN(d.getTime()) ? iso : d.toLocaleString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' })
  })
}

/** Parse one [Zeitabrechnung] block for rate / amount / note.
 *  "Direktbetrag" = an amount booked without time (may be negative — a credit),
 *  so the amount capture must keep a leading minus. */
const parseZeitabrechnung = (text: string) => {
  const rateMatch = text.match(/Stundensatz:\s*([\d.,]+)/)
  const amountMatch = text.match(/(?:Direktbetrag|Berechneter Betrag|Neuer Gesamtbetrag):\s*(-?\s?[\d.,]+)/)
  const timeMatch = text.match(/Zeit erfasst:\s*([^\n]+)/)
  const noteMatch = text.match(/Notiz:\s*\n?([\s\S]*)$/)
  return {
    rate: rateMatch ? parseGermanAmount(rateMatch[1]) : null,
    amount: amountMatch ? parseGermanAmount(amountMatch[1]) : null,
    timeText: timeMatch ? timeMatch[1].trim() : '',
    note: noteMatch ? noteMatch[1].trim().substring(0, 200) : ''
  }
}

export const useRequestReportPdf = () => {
  const buildRequestReportPdf = async (requestId: string | number, header: ReportHeader) => {
    // ---- Fetch the two trustworthy streams --------------------------------
    const messages: any[] = []
    const timeEntries: any[] = []

    try {
      const updRes: any = await $fetch(`/api/requests/requests/${requestId}/request-update`, {
        headers: useRequestHeaders(['cookie'])
      })
      for (const item of (updRes?.records || [])) {
        const text = (item.Result || item.Summary || '').toString().trim()
        const qtySpent = Number(item.QtySpent || 0)
        const date = item.Created || item.Updated || ''
        const author = item.CreatedBy?.identifier || item.UpdatedBy?.identifier || 'System'
        const isBillingBlock = /\[Zeitabrechnung\]/.test(text)
        // Staff-only notes (ConfidentialTypeEntry='I') must never reach this
        // merchant-facing report — same rule the live chat UI enforces. The
        // billing block is the one deliberate exception: it's Internal too,
        // but this report's whole point is disclosing the cost breakdown to
        // the merchant on request (see "Zeiterfassung & Kosten" below).
        const isInternalNote = item.ConfidentialTypeEntry?.id === 'I' && !isBillingBlock

        if (text && !isInternalNote) {
          messages.push({ text, author, date, qtySpent })
        }

        if (qtySpent > 0 || isBillingBlock) {
          const parsed = parseZeitabrechnung(text)
          timeEntries.push({
            date,
            author,
            minutes: qtySpent,
            timeText: parsed.timeText || (qtySpent > 0 ? formatMinutes(qtySpent) : ''),
            rate: parsed.rate,
            amount: parsed.amount,
            note: parsed.note
          })
        }
      }
    } catch (e) { /* stream unavailable -> report still renders */ }

    try {
      const histRes: any = await $fetch(`/api/requests/requests/${requestId}/request-history`, {
        headers: useRequestHeaders(['cookie'])
      })
      const sorted = [...(histRes?.records || [])].sort((a: any, b: any) =>
        new Date(a.Created || a.Updated || 0).getTime() - new Date(b.Created || b.Updated || 0).getTime()
      )
      // Same prefix-diff as the internal export: history rows carry the FULL
      // summary each time; only the newly added tail is a real step.
      let prevSummary = ''
      for (const item of sorted) {
        const fullSummary = (item.Summary || '').toString()
        let text = fullSummary
        if (prevSummary && fullSummary.startsWith(prevSummary)) {
          const added = fullSummary.slice(prevSummary.length).replace(/^\n+/, '').trim()
          text = added || ''
        }
        if (text.trim()) {
          messages.push({
            text: text.trim(),
            author: item.CreatedBy?.identifier || item.UpdatedBy?.identifier || 'System',
            date: item.Created || item.Updated || '',
            qtySpent: 0
          })
        }
        if (fullSummary.trim()) prevSummary = fullSummary
      }
    } catch (e) { /* skip */ }

    messages.sort((a, b) => (new Date(a.date || 0).getTime() || 0) - (new Date(b.date || 0).getTime() || 0))
    timeEntries.sort((a, b) => (new Date(a.date || 0).getTime() || 0) - (new Date(b.date || 0).getTime() || 0))

    // ---- Header info table ------------------------------------------------
    const infoPairs: Array<[string, string]> = [
      ['Ticket-Nr.', header.documentNo],
      ['Betreff', humanizeMarkers(header.summary || '').substring(0, 500)],
      ['Partner', header.partner],
      ['Organisation', header.organization],
      ['Kontakt', header.contact],
      ['Bearbeiter', header.salesRep],
      ['Typ', header.requestType],
      ['Kategorie', header.category],
      ['Status', header.status],
      ['Lösung', header.resolution],
      ['Priorität', header.priority],
      ['Erstellt', formatDateTime(header.created)],
      ['Letzte Aktion', formatDateTime(header.dateLastAction)],
      ['Startdatum', formatDateTime(header.startDate)],
      ['Enddatum', formatDateTime(header.endDate)]
    ]
    const infoRows = infoPairs
      .filter(([, v]) => v && String(v).trim() !== '' && String(v).trim() !== '-')
      .map(([label, value]) => ([
        { text: label, style: 'fieldLabel' },
        { text: String(value), style: 'fieldValue' }
      ]))

    // ---- Time/cost table --------------------------------------------------
    const sumMinutes = timeEntries.reduce((acc, e) => acc + (e.minutes || 0), 0)
    const sumAmount = timeEntries.reduce((acc, e) => acc + (e.amount || 0), 0)

    const costBody: any[] = [[
      { text: 'Datum', style: 'th' },
      { text: 'Dauer', style: 'th' },
      { text: 'Stundensatz', style: 'th', alignment: 'right' },
      { text: 'Betrag', style: 'th', alignment: 'right' },
      { text: 'Notiz', style: 'th' }
    ]]
    for (const entry of timeEntries) {
      costBody.push([
        { text: formatDateTime(entry.date), fontSize: 8 },
        { text: entry.timeText || (entry.minutes ? formatMinutes(entry.minutes) : ''), fontSize: 8 },
        { text: entry.rate !== null && entry.rate !== undefined ? formatEur(entry.rate) + '/h' : '', fontSize: 8, alignment: 'right' },
        { text: formatEur(entry.amount), fontSize: 8, alignment: 'right' },
        { text: entry.note || '', fontSize: 8 }
      ])
    }
    if (timeEntries.length) {
      costBody.push([
        { text: 'Summe Einträge', bold: true, fontSize: 8 },
        { text: formatMinutes(sumMinutes), bold: true, fontSize: 8 },
        { text: '', fontSize: 8 },
        { text: sumAmount > 0 ? formatEur(sumAmount) : '', bold: true, fontSize: 8, alignment: 'right' },
        { text: '', fontSize: 8 }
      ])
    }
    costBody.push([
      { text: 'Gesamt laut Ticket', bold: true, fontSize: 8, fillColor: '#eef2ff' },
      { text: formatMinutes(header.qtySpentTotal || 0), bold: true, fontSize: 8, fillColor: '#eef2ff' },
      { text: '', fillColor: '#eef2ff' },
      { text: formatEur(header.requestAmtTotal || 0), bold: true, fontSize: 8, alignment: 'right', fillColor: '#eef2ff' },
      { text: '', fillColor: '#eef2ff' }
    ])

    // ---- Timeline grouped by day ------------------------------------------
    const timelineContent: any[] = []
    let currentDay = ''
    for (const message of messages) {
      const day = formatDay(message.date)
      if (day && day !== currentDay) {
        currentDay = day
        timelineContent.push({
          text: day,
          style: 'dayHeader',
          margin: [0, 12, 0, 4]
        })
      }
      timelineContent.push({
        margin: [0, 2, 0, 6],
        table: {
          widths: ['*'],
          body: [[{
            stack: [
              {
                text: `${formatTime(message.date)} — ${message.author}` + (message.qtySpent > 0 ? `  ·  ⏱ ${formatMinutes(message.qtySpent)}` : ''),
                style: 'msgMeta'
              },
              { text: humanizeMarkers(message.text), style: 'msgText' }
            ],
            fillColor: '#fafafa'
          }]]
        },
        layout: {
          hLineWidth: () => 0.5, vLineWidth: () => 0.5,
          hLineColor: () => '#e5e7eb', vLineColor: () => '#e5e7eb',
          paddingLeft: () => 8, paddingRight: () => 8,
          paddingTop: () => 5, paddingBottom: () => 5
        }
      })
    }

    // ---- Document ---------------------------------------------------------
    const now = new Date()
    const docDefinition: any = {
      pageSize: 'A4',
      pageMargins: [40, 50, 40, 60],
      footer: (currentPage: number, pageCount: number) => ({
        columns: [
          { text: `Ticket ${header.documentNo} — erstellt am ${now.toLocaleString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' })}`, fontSize: 7, color: '#9ca3af', margin: [40, 0, 0, 0] },
          { text: `Seite ${currentPage} von ${pageCount}`, fontSize: 7, color: '#9ca3af', alignment: 'right', margin: [0, 0, 40, 0] }
        ],
        margin: [0, 20, 0, 0]
      }),
      content: [
        { text: 'Ticket-Dokumentation', fontSize: 18, bold: true, margin: [0, 0, 0, 2] },
        { text: `${header.documentNo}${header.partner ? ' · ' + header.partner : ''}`, fontSize: 11, color: '#6b7280', margin: [0, 0, 0, 16] },

        { text: 'Allgemeine Informationen', style: 'sectionHeader', margin: [0, 0, 0, 8] },
        {
          table: { widths: [130, '*'], body: infoRows },
          layout: {
            fillColor: (rowIndex: number) => rowIndex % 2 === 0 ? '#fafafa' : null,
            hLineWidth: () => 0.5, vLineWidth: () => 0.5,
            hLineColor: () => '#e5e7eb', vLineColor: () => '#e5e7eb',
            paddingLeft: () => 8, paddingRight: () => 8,
            paddingTop: () => 5, paddingBottom: () => 5
          },
          margin: [0, 0, 0, 20]
        },

        { text: 'Zeiterfassung & Kosten', style: 'sectionHeader', margin: [0, 0, 0, 8] },
        ...(timeEntries.length === 0 && !(header.qtySpentTotal > 0 || Number(header.requestAmtTotal || 0) !== 0)
          ? [{ text: 'Keine Zeit-/Kosteneinträge vorhanden.', fontSize: 9, italics: true, color: '#6b7280', margin: [0, 0, 0, 20] }]
          : [{
              table: { headerRows: 1, widths: [80, 60, 70, 70, '*'], body: costBody },
              layout: {
                hLineWidth: () => 0.5, vLineWidth: () => 0.5,
                hLineColor: () => '#e5e7eb', vLineColor: () => '#e5e7eb',
                paddingLeft: () => 6, paddingRight: () => 6,
                paddingTop: () => 4, paddingBottom: () => 4
              },
              margin: [0, 0, 0, 20]
            }]),

        { text: 'Verlauf', style: 'sectionHeader', margin: [0, 0, 0, 4] },
        ...(timelineContent.length
          ? timelineContent
          : [{ text: 'Keine Einträge vorhanden.', fontSize: 9, italics: true, color: '#6b7280' }])
      ],
      styles: {
        sectionHeader: { fontSize: 12, bold: true, color: '#111827' },
        fieldLabel: { fontSize: 8.5, bold: true, color: '#374151' },
        fieldValue: { fontSize: 8.5 },
        th: { bold: true, fontSize: 8, fillColor: '#f0f0f0' },
        dayHeader: { fontSize: 9.5, bold: true, color: '#4b5563' },
        msgMeta: { fontSize: 7.5, color: '#6b7280', margin: [0, 0, 0, 2] },
        msgText: { fontSize: 8.5, lineHeight: 1.25 }
      },
      defaultStyle: { fontSize: 9 }
    }

    const pdfMake = await loadPdfMake()
    return pdfMake.createPdf(docDefinition)
  }

  return { buildRequestReportPdf }
}
