import { jsPDF } from 'jspdf'
import { autoTable } from 'jspdf-autotable'
import html2canvas from 'html2canvas'

// =========================================================================
// Brand constants
// =========================================================================
const COLORS = {
  darkBlue: '#053d57',
  red: '#de0f1e',
  teal: '#557f8c',
  lightGray: '#f0f4f8',
  white: '#ffffff',
  textDark: '#1a1a1a',
  textMuted: '#888888',
} as const

const DARK_BLUE_RGB: [number, number, number] = [5, 61, 87]
const TEAL_RGB: [number, number, number] = [85, 127, 140]
const RED_RGB: [number, number, number] = [222, 15, 30]
const LIGHT_GRAY_RGB: [number, number, number] = [240, 244, 248]

const PAGE_WIDTH = 210 // A4 mm
const PAGE_HEIGHT = 297
const MARGIN = 15
const CONTENT_WIDTH = PAGE_WIDTH - MARGIN * 2

// =========================================================================
// Font & logo caching
// =========================================================================
let cachedFonts: { regular: string; bold: string } | null = null
let cachedLogo: string | null = null
let fontLoadAttempted = false
let logoLoadAttempted = false

async function loadFonts(): Promise<{ regular: string; bold: string } | null> {
  if (cachedFonts) return cachedFonts
  if (fontLoadAttempted) return null
  fontLoadAttempted = true
  try {
    const [regularRes, boldRes] = await Promise.all([
      fetch('/fonts/Montserrat-Regular.ttf'),
      fetch('/fonts/Montserrat-Bold.ttf'),
    ])
    if (!regularRes.ok || !boldRes.ok) return null
    const [regularBuf, boldBuf] = await Promise.all([
      regularRes.arrayBuffer(),
      boldRes.arrayBuffer(),
    ])
    const toBase64 = (buf: ArrayBuffer) => {
      const bytes = new Uint8Array(buf)
      let binary = ''
      for (let i = 0; i < bytes.length; i++) {
        binary += String.fromCharCode(bytes[i])
      }
      return btoa(binary)
    }
    cachedFonts = { regular: toBase64(regularBuf), bold: toBase64(boldBuf) }
    return cachedFonts
  } catch {
    return null
  }
}

async function loadLogo(): Promise<string | null> {
  if (cachedLogo) return cachedLogo
  if (logoLoadAttempted) return null
  logoLoadAttempted = true
  try {
    const res = await fetch('/images/MOPC-blue-long.png')
    if (!res.ok) return null
    const blob = await res.blob()
    return new Promise((resolve) => {
      const reader = new FileReader()
      reader.onloadend = () => {
        cachedLogo = reader.result as string
        resolve(cachedLogo)
      }
      reader.onerror = () => resolve(null)
      reader.readAsDataURL(blob)
    })
  } catch {
    return null
  }
}

// =========================================================================
// Document creation
// =========================================================================
export interface ReportDocumentOptions {
  orientation?: 'portrait' | 'landscape'
}

export async function createReportDocument(
  options?: ReportDocumentOptions
): Promise<jsPDF> {
  const doc = new jsPDF({
    orientation: options?.orientation || 'portrait',
    unit: 'mm',
    format: 'a4',
  })

  // Load and register fonts
  const fonts = await loadFonts()
  if (fonts) {
    doc.addFileToVFS('Montserrat-Regular.ttf', fonts.regular)
    doc.addFont('Montserrat-Regular.ttf', 'Montserrat', 'normal')
    doc.addFileToVFS('Montserrat-Bold.ttf', fonts.bold)
    doc.addFont('Montserrat-Bold.ttf', 'Montserrat', 'bold')
    doc.setFont('Montserrat', 'normal')
  } else {
    doc.setFont('helvetica', 'normal')
  }

  return doc
}

// =========================================================================
// Cover page
// =========================================================================
export interface CoverPageOptions {
  title: string
  subtitle?: string
  roundName?: string
  programName?: string
}

export async function addCoverPage(
  doc: jsPDF,
  options: CoverPageOptions
): Promise<void> {
  const logo = await loadLogo()

  // Logo centered
  if (logo) {
    const logoWidth = 80
    const logoHeight = 20
    const logoX = (PAGE_WIDTH - logoWidth) / 2
    doc.addImage(logo, 'PNG', logoX, 60, logoWidth, logoHeight)
  }

  // Title
  const fontName = getFont(doc)
  doc.setFont(fontName, 'bold')
  doc.setFontSize(24)
  doc.setTextColor(...DARK_BLUE_RGB)
  doc.text(options.title, PAGE_WIDTH / 2, logo ? 110 : 100, { align: 'center' })

  // Subtitle
  if (options.subtitle) {
    doc.setFont(fontName, 'normal')
    doc.setFontSize(14)
    doc.setTextColor(...TEAL_RGB)
    doc.text(options.subtitle, PAGE_WIDTH / 2, logo ? 125 : 115, { align: 'center' })
  }

  // Round & program
  let infoY = logo ? 145 : 135
  doc.setFontSize(12)
  doc.setTextColor(...DARK_BLUE_RGB)

  if (options.programName) {
    doc.text(options.programName, PAGE_WIDTH / 2, infoY, { align: 'center' })
    infoY += 8
  }
  if (options.roundName) {
    doc.setFont(fontName, 'bold')
    doc.text(options.roundName, PAGE_WIDTH / 2, infoY, { align: 'center' })
    infoY += 8
  }

  // Date
  doc.setFont(fontName, 'normal')
  doc.setFontSize(10)
  doc.setTextColor(136, 136, 136)
  doc.text(
    `Generated on ${new Date().toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' })}`,
    PAGE_WIDTH / 2,
    infoY + 10,
    { align: 'center' }
  )

  // Decorative line
  doc.setDrawColor(...TEAL_RGB)
  doc.setLineWidth(0.5)
  doc.line(MARGIN + 30, infoY + 20, PAGE_WIDTH - MARGIN - 30, infoY + 20)
}

// =========================================================================
// Header (on content pages)
// =========================================================================
export async function addHeader(doc: jsPDF, title: string): Promise<void> {
  const logo = await loadLogo()

  if (logo) {
    doc.addImage(logo, 'PNG', MARGIN, 8, 30, 8)
  }

  const fontName = getFont(doc)
  doc.setFont(fontName, 'bold')
  doc.setFontSize(11)
  doc.setTextColor(...DARK_BLUE_RGB)
  doc.text(title, PAGE_WIDTH / 2, 14, { align: 'center' })

  doc.setFont(fontName, 'normal')
  doc.setFontSize(8)
  doc.setTextColor(136, 136, 136)
  doc.text(
    new Date().toLocaleDateString('en-GB'),
    PAGE_WIDTH - MARGIN,
    14,
    { align: 'right' }
  )

  // Line under header
  doc.setDrawColor(...TEAL_RGB)
  doc.setLineWidth(0.3)
  doc.line(MARGIN, 18, PAGE_WIDTH - MARGIN, 18)
}

// =========================================================================
// Footer
// =========================================================================
export function addFooter(
  doc: jsPDF,
  pageNumber: number,
  totalPages: number
): void {
  const fontName = getFont(doc)
  const y = PAGE_HEIGHT - 10

  doc.setFont(fontName, 'normal')
  doc.setFontSize(7)
  doc.setTextColor(136, 136, 136)

  doc.text('Generated by MOPC Platform', MARGIN, y)
  doc.text('Confidential', PAGE_WIDTH / 2, y, { align: 'center' })
  doc.text(`Page ${pageNumber} of ${totalPages}`, PAGE_WIDTH - MARGIN, y, {
    align: 'right',
  })
}

export function addAllPageFooters(doc: jsPDF): void {
  const totalPages = doc.getNumberOfPages()
  for (let i = 1; i <= totalPages; i++) {
    doc.setPage(i)
    addFooter(doc, i, totalPages)
  }
}

// =========================================================================
// Section title
// =========================================================================
export function addSectionTitle(doc: jsPDF, title: string, y: number): number {
  const fontName = getFont(doc)

  doc.setFont(fontName, 'bold')
  doc.setFontSize(16)
  doc.setTextColor(...DARK_BLUE_RGB)
  doc.text(title, MARGIN, y)

  // Teal underline
  doc.setDrawColor(...TEAL_RGB)
  doc.setLineWidth(0.5)
  doc.line(MARGIN, y + 2, MARGIN + doc.getTextWidth(title), y + 2)

  return y + 12
}

// =========================================================================
// Stat cards row
// =========================================================================
export function addStatCards(
  doc: jsPDF,
  stats: Array<{ label: string; value: string | number }>,
  y: number
): number {
  const fontName = getFont(doc)
  const cardCount = Math.min(stats.length, 4)
  const gap = 4
  const cardWidth = (CONTENT_WIDTH - gap * (cardCount - 1)) / cardCount
  const cardHeight = 22

  for (let i = 0; i < cardCount; i++) {
    const x = MARGIN + i * (cardWidth + gap)

    // Card background
    doc.setFillColor(...LIGHT_GRAY_RGB)
    doc.roundedRect(x, y, cardWidth, cardHeight, 2, 2, 'F')

    // Value
    doc.setFont(fontName, 'bold')
    doc.setFontSize(18)
    doc.setTextColor(...DARK_BLUE_RGB)
    doc.text(String(stats[i].value), x + cardWidth / 2, y + 10, {
      align: 'center',
    })

    // Label
    doc.setFont(fontName, 'normal')
    doc.setFontSize(8)
    doc.setTextColor(...TEAL_RGB)
    doc.text(stats[i].label, x + cardWidth / 2, y + 18, { align: 'center' })
  }

  return y + cardHeight + 8
}

// =========================================================================
// Table via autoTable
// =========================================================================
export function addTable(
  doc: jsPDF,
  headers: string[],
  rows: (string | number)[][],
  y: number
): number {
  const fontName = getFont(doc)

  autoTable(doc, {
    startY: y,
    head: [headers],
    body: rows,
    margin: { left: MARGIN, right: MARGIN },
    styles: {
      font: fontName,
      fontSize: 9,
      cellPadding: 3,
      textColor: [26, 26, 26],
    },
    headStyles: {
      fillColor: DARK_BLUE_RGB,
      textColor: [255, 255, 255],
      fontStyle: 'bold',
      fontSize: 9,
    },
    alternateRowStyles: {
      fillColor: [248, 248, 248],
    },
    theme: 'grid',
    tableLineColor: [220, 220, 220],
    tableLineWidth: 0.1,
  })

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const finalY = (doc as any).lastAutoTable?.finalY ?? y + 20
  return finalY + 8
}

// =========================================================================
// Chart image capture
// =========================================================================
export async function addChartImage(
  doc: jsPDF,
  element: HTMLElement,
  y: number,
  options?: { maxHeight?: number }
): Promise<number> {
  const canvas = await html2canvas(element, {
    scale: 2,
    useCORS: true,
    backgroundColor: COLORS.white,
    logging: false,
  })

  const imgData = canvas.toDataURL('image/jpeg', 0.95)
  const imgWidth = CONTENT_WIDTH
  const ratio = canvas.height / canvas.width
  let imgHeight = imgWidth * ratio
  const maxH = options?.maxHeight || 100

  if (imgHeight > maxH) {
    imgHeight = maxH
  }

  // Check page break
  y = checkPageBreak(doc, y, imgHeight + 5)

  doc.addImage(imgData, 'JPEG', MARGIN, y, imgWidth, imgHeight)
  return y + imgHeight + 8
}

// =========================================================================
// Page break helper
// =========================================================================
export function checkPageBreak(
  doc: jsPDF,
  y: number,
  neededHeight: number
): number {
  const availableHeight = PAGE_HEIGHT - 20 // leave room for footer
  if (y + neededHeight > availableHeight) {
    doc.addPage()
    return 25 // start below header area
  }
  return y
}

export function addPageBreak(doc: jsPDF): void {
  doc.addPage()
}

// =========================================================================
// Save
// =========================================================================
export function savePdf(doc: jsPDF, filename: string): void {
  doc.save(filename)
}

// =========================================================================
// Helper
// =========================================================================
function getFont(doc: jsPDF): string {
  // Check if Montserrat was loaded
  try {
    const fonts = doc.getFontList()
    if (fonts['Montserrat']) return 'Montserrat'
  } catch {
    // Fallback
  }
  return 'helvetica'
}
