import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { PDFCacheManager } from '../cache-manager'
import { PDFRenderer } from '../renderer'
import { PDFExportDatabaseClient } from '../db-client'
import { renderTemplate } from '../renderer'
import type { PDFTemplateData } from '../types'
import type { Pool } from 'pg'

// Mock Playwright for renderer tests
vi.mock('playwright', () => {
  const mockPage = {
    setContent: vi.fn().mockResolvedValue(undefined),
    pdf: vi.fn().mockResolvedValue(Buffer.from('pdf binary content')),
    close: vi.fn().mockResolvedValue(undefined),
  }

  const mockBrowser = {
    newPage: vi.fn().mockResolvedValue(mockPage),
    close: vi.fn().mockResolvedValue(undefined),
  }

  return {
    chromium: {
      launch: vi.fn().mockResolvedValue(mockBrowser),
    },
  }
})

describe('PDF Export Integration', () => {
  let cache: PDFCacheManager
  let renderer: PDFRenderer
  let mockPool: Partial<Pool>
  let db: PDFExportDatabaseClient

  const testData: PDFTemplateData = {
    asn: 13335,
    network_name: 'Cloudflare Inc.',
    generated_at: '2026-04-29T10:00:00Z',
    health_score: {
      overall: 85,
      aspa: 72,
      rpki: 90,
      bgp_stability: 88,
      peering_health: 80,
    },
    aspa: {
      adoption_status: 'in_progress',
      provider_verification: 55,
      readiness_score: 70,
    },
    peering: {
      ixp_connections: 15,
      peer_count: 45,
      open_peers: 32,
      route_exports: 1500,
    },
    threats: {
      recent_hijacks: 0,
      anomalies_detected: 1,
      rpki_invalids: 2,
      moas_events: 0,
    },
    recommendations: [
      'Maintain RPKI ROV implementation',
      'Continue ASPA provider adoption',
      'Monitor anomalies monthly',
    ],
    data_sources: [
      'RIPE RIS Route Collectors',
      'RouteViews BGP Archive',
      'RPKI Repository Objects',
    ],
  }

  beforeEach(() => {
    cache = new PDFCacheManager()
    renderer = new PDFRenderer()
    mockPool = {
      query: vi.fn(),
    }
    db = new PDFExportDatabaseClient(mockPool as Pool)
    vi.useFakeTimers()
  })

  afterEach(async () => {
    cache.destroy()
    await renderer.close()
    vi.useRealTimers()
    vi.clearAllMocks()
  })

  describe('Full PDF generation workflow', () => {
    it('should render template, cache result, and record in database', async () => {
      const dateStr = '2026-04-29'
      const format = 'report'
      const cacheKey = cache.generateKey(13335, format, dateStr)

      // Step 1: Render template
      const htmlTemplate = `
        <html>
          <head><title>{{network_name}} Report</title></head>
          <body>
            <h1>{{network_name}}</h1>
            <p>ASN: {{asn}}</p>
            <p>Health Score: {{health_score.overall}}</p>
            <ul>
              {{#each recommendations}}<li>{{this}}</li>{{/each}}
            </ul>
          </body>
        </html>
      `

      const renderedHtml = renderTemplate(htmlTemplate, testData)

      expect(renderedHtml).toContain('Cloudflare Inc.')
      expect(renderedHtml).toContain('ASN: 13335')
      expect(renderedHtml).toContain('Health Score: 85')
      expect(renderedHtml).toContain('Maintain RPKI ROV implementation')

      // Step 2: Render PDF
      const pdfBuffer = await renderer.renderPDF(renderedHtml)

      expect(pdfBuffer).toBeInstanceOf(Buffer)
      expect(pdfBuffer.length).toBeGreaterThan(0)

      // Step 3: Cache the PDF
      cache.set(cacheKey, pdfBuffer)

      const cachedEntry = cache.get(cacheKey)
      expect(cachedEntry).not.toBeNull()
      expect(cachedEntry?.pdfBuffer).toEqual(pdfBuffer)

      // Step 4: Record in database
      ;(mockPool.query as any).mockResolvedValueOnce({
        rows: [
          {
            id: 1,
            resource_type: 'asn',
            resource_value: 'AS13335',
            format: 'report',
            generated_at: new Date(),
            expires_at: new Date(Date.now() + 5 * 60 * 1000),
            file_hash: cachedEntry!.hash,
            file_size_bytes: pdfBuffer.length,
            metadata: { format },
          },
        ],
        rowCount: 1,
      })

      const dbRecord = await db.recordPDFGeneration(
        'AS13335',
        format,
        cachedEntry!.hash,
        pdfBuffer.length,
        { format }
      )

      expect(dbRecord.id).toBe(1)
      expect(dbRecord.file_hash).toBe(cachedEntry!.hash)
      expect(dbRecord.file_size_bytes).toBe(pdfBuffer.length)
    })
  })

  describe('Cache hit optimization', () => {
    it('should return cached PDF without re-rendering', async () => {
      const dateStr = '2026-04-29'
      const format = 'report'
      const cacheKey = cache.generateKey(13335, format, dateStr)
      const pdfBuffer = Buffer.from('cached pdf content')

      // Store PDF in cache
      cache.set(cacheKey, pdfBuffer)
      const cachedEntry1 = cache.get(cacheKey)

      // Request same PDF again (cache hit)
      const cachedEntry2 = cache.get(cacheKey)

      expect(cachedEntry1).not.toBeNull()
      expect(cachedEntry2).not.toBeNull()
      expect(cachedEntry2!.pdfBuffer).toEqual(pdfBuffer)
      expect(cachedEntry2!.hits).toBe(2)
    })

    it('should expire cached PDF after TTL', async () => {
      const dateStr = '2026-04-29'
      const format = 'report'
      const cacheKey = cache.generateKey(13335, format, dateStr)
      const pdfBuffer = Buffer.from('cached pdf content')

      cache.set(cacheKey, pdfBuffer)
      expect(cache.get(cacheKey)).not.toBeNull()

      // Advance time past TTL (5 minutes)
      vi.advanceTimersByTime(5 * 60 * 1000 + 1000)

      expect(cache.get(cacheKey)).toBeNull()
    })
  })

  describe('Multiple formats on same day', () => {
    it('should handle three different formats for same ASN/date', async () => {
      const dateStr = '2026-04-29'
      const asn = 13335
      const formats = ['report', 'executive', 'technical']

      const pdfs: Map<string, Buffer> = new Map()

      // Generate and cache PDFs for all three formats
      for (const format of formats) {
        const cacheKey = cache.generateKey(asn, format, dateStr)
        const pdfBuffer = Buffer.from(`pdf content for ${format}`)

        cache.set(cacheKey, pdfBuffer)
        pdfs.set(format, pdfBuffer)
      }

      // Verify all are cached
      for (const format of formats) {
        const cacheKey = cache.generateKey(asn, format, dateStr)
        const cached = cache.get(cacheKey)

        expect(cached).not.toBeNull()
        expect(cached?.pdfBuffer).toEqual(pdfs.get(format))
      }

      // Verify cache stats
      const stats = cache.getStats()
      expect(stats.size).toBe(3)
    })
  })

  describe('Large PDF handling', () => {
    it('should track memory usage for large PDFs', async () => {
      const largeBuffer = Buffer.from('x'.repeat(10 * 1024 * 1024)) // 10MB
      const smallBuffer = Buffer.from('y'.repeat(1 * 1024 * 1024)) // 1MB

      cache.set('large', largeBuffer)
      cache.set('small', smallBuffer)

      const stats = cache.getStats()

      expect(stats.totalMemory).toBe(11 * 1024 * 1024)
      expect(stats.size).toBe(2)
    })

    it('should handle PDF size tracking in database', async () => {
      const largeBuffer = Buffer.from('x'.repeat(50 * 1024 * 1024)) // 50MB

      ;(mockPool.query as any).mockResolvedValueOnce({
        rows: [
          {
            id: 1,
            resource_type: 'asn',
            resource_value: 'AS13335',
            format: 'report',
            generated_at: new Date(),
            expires_at: new Date(),
            file_hash: 'largehash',
            file_size_bytes: largeBuffer.length,
            metadata: {},
          },
        ],
        rowCount: 1,
      })

      const record = await db.recordPDFGeneration(
        'AS13335',
        'report',
        'largehash',
        largeBuffer.length,
        {}
      )

      expect(record.file_size_bytes).toBe(50 * 1024 * 1024)
    })
  })

  describe('Deduplication by hash', () => {
    it('should detect duplicate PDFs by hash', async () => {
      const buffer = Buffer.from('same content')
      const hash1 = require('crypto').createHash('sha256').update(buffer).digest('hex')

      // First PDF recorded
      ;(mockPool.query as any).mockResolvedValueOnce({
        rows: [
          {
            id: 1,
            resource_value: 'AS13335',
            file_hash: hash1,
            file_size_bytes: buffer.length,
          },
        ],
        rowCount: 1,
      })

      const record1 = await db.recordPDFGeneration('AS13335', 'report', hash1, buffer.length)

      // Try to get same PDF by hash
      ;(mockPool.query as any).mockResolvedValueOnce({
        rows: [
          {
            id: 1,
            resource_value: 'AS13335',
            file_hash: hash1,
            file_size_bytes: buffer.length,
            expires_at: new Date(Date.now() + 5 * 60 * 1000),
          },
        ],
        rowCount: 1,
      })

      const record2 = await db.getPDFByHash(hash1)

      expect(record1.file_hash).toBe(record2?.file_hash)
    })
  })

  describe('Database cleanup', () => {
    it('should cleanup expired PDF records', async () => {
      ;(mockPool.query as any).mockResolvedValueOnce({
        rowCount: 5,
      })

      const deleted = await db.cleanupExpiredPDFs()

      expect(deleted).toBe(5)
    })

    it('should get statistics before cleanup', async () => {
      ;(mockPool.query as any).mockResolvedValueOnce({
        rows: [
          {
            total_records: 50,
            total_size_bytes: 100000000,
            expired_records: 5,
          },
        ],
        rowCount: 1,
      })

      const stats = await db.getPDFStats()

      expect(stats.total_records).toBe(50)
      expect(stats.expired_records).toBe(5)
    })
  })

  describe('Error recovery', () => {
    it('should handle rendering failures gracefully', async () => {
      // Rendering can fail if HTML is malformed
      const malformedTemplate = 'No template variables here'
      const result = renderTemplate(malformedTemplate, testData)

      expect(result).toBe(malformedTemplate)
    })

    it('should recover from cache misses', async () => {
      const cacheKey = cache.generateKey(13335, 'report', '2026-04-29')

      // Cache miss
      const cached = cache.get(cacheKey)
      expect(cached).toBeNull()

      // Should be able to generate new PDF
      const htmlTemplate = '<html><body>{{network_name}}</body></html>'
      const rendered = renderTemplate(htmlTemplate, testData)

      expect(rendered).toContain('Cloudflare Inc.')
    })
  })
})
