/**
 * PDF Browser Service (pdfmake)
 *
 * Browser-compatible PDF rendering using pdfmake.
 * Generates PDFs in the browser without server round-trips.
 *
 * Uses Roboto fonts from vfs_fonts for consistent rendering.
 *
 * @see https://pdfmake.github.io/docs/
 */

import type { TFontDictionary, TCreatedPdf, TDocumentDefinitions } from 'pdfmake/interfaces'

import { defaultTheme } from '../themes'
import type { PDFTheme } from '../themes'
import type { PDFDocument } from '../types'
import { convertDocument } from '../shared/convert'

// ============================================================================
// Lazy pdfmake initialization
// ============================================================================

interface PdfMakeInstance {
  createPdf: (docDefinition: TDocumentDefinitions) => TCreatedPdf
  setFonts?: (fonts: TFontDictionary) => void
  addVirtualFileSystem?: (vfs: Record<string, string>) => void
}

// pdfmake module reference (loaded on first use)
let pdfMakeModule: PdfMakeInstance | null = null
let fontsConfigured = false

/**
 * Get pdfmake module with lazy initialization
 * This avoids SSR issues and ensures proper browser-side loading
 */
async function getPdfMake(): Promise<PdfMakeInstance> {
  if (pdfMakeModule) {
    return pdfMakeModule
  }

  // Dynamic import to ensure browser-side loading
  const [pdfMakeImport, pdfFontsImport] = await Promise.all([
    import('pdfmake/build/pdfmake'),
    import('pdfmake/build/vfs_fonts')
  ])

  // Handle both default and named exports (CJS/ESM interop)
  const mod = ((pdfMakeImport as { default?: PdfMakeInstance }).default ?? pdfMakeImport) as PdfMakeInstance
  pdfMakeModule = mod

  // Add virtual file system with font data
  const vfsModule = pdfFontsImport as Record<string, unknown>
  const vfsKeys = Object.keys(vfsModule).filter((k) => k !== 'default')

  // vfs_fonts exports font files directly at top level (e.g., 'Roboto-Regular.ttf': base64data)
  // Build VFS object from the import, excluding 'default' key
  const vfsData: Record<string, string> = {}
  for (const key of vfsKeys) {
    if (typeof vfsModule[key] === 'string') {
      vfsData[key] = vfsModule[key]
    }
  }

  if (Object.keys(vfsData).length > 0 && mod.addVirtualFileSystem) {
    mod.addVirtualFileSystem(vfsData)
  }

  // Set fonts on first load (use Roboto from vfs_fonts)
  if (!fontsConfigured && pdfMakeModule.setFonts) {
    pdfMakeModule.setFonts({
      Roboto: {
        normal: 'Roboto-Regular.ttf',
        bold: 'Roboto-Medium.ttf',
        italics: 'Roboto-Italic.ttf',
        bolditalics: 'Roboto-MediumItalic.ttf'
      }
    })
    fontsConfigured = true
  }

  return pdfMakeModule
}

// ============================================================================
// Browser Service Class
// ============================================================================

/**
 * PdfMakeBrowserService
 *
 * Browser-compatible PDF generation service using pdfmake.
 * Generates PDFs client-side and returns them as data URLs, blobs, or triggers downloads.
 */
export class PdfMakeBrowserService {
  /**
   * Generate PDF and return as data URL for iframe src
   *
   * @param document - PDFDocument schema to render
   * @param theme - Optional theme override (merged with defaults)
   * @returns Data URL string (e.g., "data:application/pdf;base64,...")
   */
  async generateDataUrl(document: PDFDocument, theme?: Partial<PDFTheme>): Promise<string> {
    const pdfMake = await getPdfMake()
    if (!pdfMake) throw new Error('pdfmake failed to load')

    const mergedTheme = this.mergeTheme(theme)
    const docDefinition = convertDocument(document, mergedTheme)
    const pdf = pdfMake.createPdf(docDefinition)

    // pdfmake browser API uses callbacks, wrap in Promise
    // Note: @types/pdfmake doesn't properly type callback overloads
    return new Promise<string>((resolve, reject) => {
      try {
        ;(pdf.getDataUrl as (cb: (result: string) => void) => void)((result) => {
          resolve(result)
        })
      } catch (err) {
        reject(err)
      }
    })
  }

  /**
   * Generate PDF and return as Blob for download or processing
   *
   * @param document - PDFDocument schema to render
   * @param theme - Optional theme override (merged with defaults)
   * @returns Blob containing the PDF
   */
  async generateBlob(document: PDFDocument, theme?: Partial<PDFTheme>): Promise<Blob> {
    const pdfMake = await getPdfMake()
    if (!pdfMake) throw new Error('pdfmake failed to load')

    const mergedTheme = this.mergeTheme(theme)
    const docDefinition = convertDocument(document, mergedTheme)
    const pdf = pdfMake.createPdf(docDefinition)
    // pdfmake browser API uses callbacks
    // Note: @types/pdfmake doesn't properly type callback overloads
    return new Promise<Blob>((resolve, reject) => {
      try {
        ;(pdf.getBlob as (cb: (result: Blob) => void) => void)((result) => {
          resolve(result)
        })
      } catch (err) {
        reject(err)
      }
    })
  }

  /**
   * Trigger browser download of the PDF
   *
   * @param document - PDFDocument schema to render
   * @param filename - Filename for the download (e.g., "proposal.pdf")
   * @param theme - Optional theme override (merged with defaults)
   */
  async download(document: PDFDocument, filename: string, theme?: Partial<PDFTheme>): Promise<void> {
    const pdfMake = await getPdfMake()
    if (!pdfMake) throw new Error('pdfmake failed to load')

    const mergedTheme = this.mergeTheme(theme)
    const docDefinition = convertDocument(document, mergedTheme)
    const pdf = pdfMake.createPdf(docDefinition)
    await pdf.download(filename)
  }

  /**
   * Open PDF in a new browser tab
   *
   * @param document - PDFDocument schema to render
   * @param theme - Optional theme override (merged with defaults)
   */
  async openInNewTab(document: PDFDocument, theme?: Partial<PDFTheme>): Promise<void> {
    const pdfMake = await getPdfMake()
    if (!pdfMake) throw new Error('pdfmake failed to load')

    const mergedTheme = this.mergeTheme(theme)
    const docDefinition = convertDocument(document, mergedTheme)
    const pdf = pdfMake.createPdf(docDefinition)
    await pdf.open()
  }

  /**
   * Print PDF using browser's print dialog
   *
   * @param document - PDFDocument schema to render
   * @param theme - Optional theme override (merged with defaults)
   */
  async print(document: PDFDocument, theme?: Partial<PDFTheme>): Promise<void> {
    const pdfMake = await getPdfMake()
    if (!pdfMake) throw new Error('pdfmake failed to load')

    const mergedTheme = this.mergeTheme(theme)
    const docDefinition = convertDocument(document, mergedTheme)
    const pdf = pdfMake.createPdf(docDefinition)
    await pdf.print()
  }

  // ==========================================================================
  // Private Helpers
  // ==========================================================================

  /**
   * Merge theme override with defaults
   */
  private mergeTheme(themeOverride?: Partial<PDFTheme>): PDFTheme {
    if (!themeOverride) {
      return defaultTheme
    }
    return {
      ...defaultTheme,
      ...themeOverride,
      colors: { ...defaultTheme.colors, ...themeOverride.colors },
      typography: { ...defaultTheme.typography, ...themeOverride.typography },
      spacing: { ...defaultTheme.spacing, ...themeOverride.spacing }
    }
  }
}

/**
 * Singleton instance for convenience
 *
 * Can be used directly without instantiation:
 * @example
 * import { pdfMakeBrowserService } from '@repo/core/pdf/browser'
 * const dataUrl = await pdfMakeBrowserService.generateDataUrl(document)
 */
export const pdfMakeBrowserService = new PdfMakeBrowserService()
