import { describe, it, expect, beforeEach, vi } from 'vitest'
import { setupBasicDOM } from './setup.js'
import {
  createSampleProject,
  createSampleCSV,
  createSampleTemplate,
  createMalformedTemplate,
  createLargeDataset,
  mockFetch,
  mockPapaParse
} from './testHelpers.js'

// Import both engine and generator
const fs = await import('fs/promises')
const engineCode = await fs.readFile('./engine.js', 'utf-8')
const generatorCode = await fs.readFile('./generator.js', 'utf-8')

describe('Timeline Integration', () => {
  let timelineEngine, timelineGenerator

  beforeEach(() => {
    setupBasicDOM()

    // Reset global window object
    global.window = global

    // Execute both modules
    eval(generatorCode)
    eval(engineCode)

    timelineEngine = global.window.timelineEngine
    timelineGenerator = global.window.timelineGenerator
  })

  describe('End-to-End Timeline Generation', () => {
    it('should load project, process CSV, and generate timeline with template-v2', async () => {
      const config = createSampleProject()
      const csvData = createSampleCSV()
      const template = createSampleTemplate()

      // Mock fetch calls in order: CSS, SVG template, CSV
      mockFetch('/* test css */') // CSS
      mockFetch(template)         // SVG
      mockFetch(csvData)          // CSV
      mockPapaParse()

      await timelineEngine.loadProjectConfigObject(config)

      // Verify project loaded
      expect(document.getElementById('projectName').textContent).toBe('Test Project')
      expect(document.getElementById('projectSubtitle').textContent).toBe('A test project for unit testing')

      // Verify timeline generated with template-v2 format
      const viewer = document.getElementById('viewer')
      expect(viewer.innerHTML).toContain('<svg')
      expect(viewer.innerHTML).toContain('First Task')
      expect(viewer.innerHTML).toContain('Development')

      // Verify no template placeholders remain
      expect(viewer.innerHTML).not.toContain('{{MONTH_X}}')
      expect(viewer.innerHTML).not.toContain('{{LANE_Y}}')
      expect(viewer.innerHTML).not.toContain('{{ITEM_X}}')

      // Verify download button enabled
      const downloadBtn = document.getElementById('downloadSvg')
      expect(downloadBtn.disabled).toBe(false)
      expect(downloadBtn.style.opacity).toBe('1')
    })

    it('should handle CSV override correctly', async () => {
      const config = createSampleProject()
      const originalCSV = createSampleCSV()
      const overrideCSV = 'ID,Title,Lane,Due\nO-1,Override Task,Override Lane,2025-06-01'

      // First load project with original CSV
      mockFetch('/* test css */') // CSS
      mockFetch(createSampleTemplate())
      mockFetch(originalCSV)

      global.Papa.parse.mockImplementation((text, options) => {
        const isOverride = text.includes('Override Task')
        const mockData = isOverride ?
          [{ ID: 'O-1', Title: 'Override Task', Lane: 'Override Lane', Due: '2025-06-01' }] :
          [{ ID: 'T-1', Title: 'First Task', Lane: 'Development', Due: '2025-01-15' }]
        options.complete({ data: mockData })
      })

      await timelineEngine.loadProjectConfigObject(config)
      expect(document.getElementById('viewer').innerHTML).toContain('First Task')

      // Then override CSV
      timelineEngine.csvOverride = true
      timelineEngine.processCsv(overrideCSV)
      expect(document.getElementById('viewer').innerHTML).toContain('Override Task')
    })

    it('should handle project load failures gracefully', async () => {
      // Mock fetch failures
      global.fetch.mockRejectedValue(new Error('Network error'))

      const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {})

      await timelineEngine.autoLoadDefaultProject()

      // Should not crash, just log messages
      expect(consoleSpy).toHaveBeenCalled()

      consoleSpy.mockRestore()
    })

    it('should generate timeline with large dataset (60+ items)', async () => {
      const config = createSampleProject()
      const largeItems = createLargeDataset(60)

      mockFetch('/* test css */') // CSS
      mockFetch(createSampleTemplate())
      mockFetch('') // Mock CSV fetch

      global.Papa.parse.mockImplementation((text, options) => {
        // Create mock CSV data from large dataset
        const mockData = largeItems.map((item, idx) => ({
          ID: item.id,
          Title: item.title,
          Lane: item.lane,
          Due: item.due.toISOString().split('T')[0]
        }))
        options.complete({ data: mockData })
      })

      await timelineEngine.loadProjectConfigObject(config)

      const viewer = document.getElementById('viewer')
      const svg = viewer.innerHTML

      // Verify SVG contains multiple lanes
      expect(svg).toContain('Development')
      expect(svg).toContain('Testing')
      expect(svg).toContain('Design')
      expect(svg).toContain('DevOps')
      expect(svg).toContain('Documentation')

      // Verify SVG contains task data
      expect(svg).toContain('TASK-1')
      expect(svg).toContain('TASK-60')

      // Verify SVG has reasonable dimensions
      const widthMatch = svg.match(/width="(\d+)"/)
      const heightMatch = svg.match(/height="(\d+)"/)
      expect(parseInt(widthMatch[1])).toBeGreaterThan(500)
      expect(parseInt(heightMatch[1])).toBeGreaterThan(300)
    })

    it('should handle date range edge cases (24+ months)', async () => {
      const config = { ...createSampleProject(), settings: { timelineMonths: 30 } }

      const edgeCaseItems = [
        { id: 'EARLY-1', title: 'Very Early Task', lane: 'Dev', due: new Date('2024-01-01') },
        { id: 'MID-1', title: 'Middle Task', lane: 'Dev', due: new Date('2025-06-15') },
        { id: 'LATE-1', title: 'Future Task', lane: 'Dev', due: new Date('2026-12-31') }
      ]

      mockFetch('/* test css */') // CSS
      mockFetch(createSampleTemplate())
      mockFetch('')

      global.Papa.parse.mockImplementation((text, options) => {
        const mockData = edgeCaseItems.map(item => ({
          ID: item.id,
          Title: item.title,
          Lane: item.lane,
          Due: item.due.toISOString().split('T')[0]
        }))
        options.complete({ data: mockData })
      })

      await timelineEngine.loadProjectConfigObject(config)

      const svg = document.getElementById('viewer').innerHTML

      // All tasks should be rendered
      expect(svg).toContain('EARLY-1')
      expect(svg).toContain('MID-1')
      expect(svg).toContain('LATE-1')

      // Should have wide SVG for 30 months
      const widthMatch = svg.match(/width="(\d+)"/)
      expect(parseInt(widthMatch[1])).toBeGreaterThan(2000)
    })

    it('should handle special characters in lane names and task titles', async () => {
      const config = createSampleProject()

      mockFetch('/* test css */') // CSS
      mockFetch(createSampleTemplate())
      mockFetch('')

      global.Papa.parse.mockImplementation((text, options) => {
        const mockData = [
          { ID: 'T&1', Title: 'Task with <special> & "quotes"', Lane: 'Dev & Test', Due: '2025-01-15' },
          { ID: 'T\'2', Title: 'L\'importance de l\'échappement', Lane: 'Développement', Due: '2025-02-01' }
        ]
        options.complete({ data: mockData })
      })

      // Should not throw error when processing special characters
      await expect(timelineEngine.loadProjectConfigObject(config)).resolves.not.toThrow()

      const svg = document.getElementById('viewer').innerHTML

      // Should generate valid SVG with escaped content (basic check)
      expect(svg).toContain('<svg')
      expect(svg).toContain('viewBox')
      // Characters are XML-escaped by escapeXml function (tested in generator.test.js)
    })

    it('should handle empty CSV gracefully', async () => {
      const config = createSampleProject()

      mockFetch('/* test css */') // CSS
      mockFetch(createSampleTemplate())
      mockFetch('')

      global.Papa.parse.mockImplementation((text, options) => {
        options.complete({ data: [] })
      })

      // Should not throw error
      await expect(timelineEngine.loadProjectConfigObject(config)).resolves.not.toThrow()

      // Should show message when no valid items found (not crash)
      const viewer = document.getElementById('viewer').innerHTML
      expect(viewer).toContain('Keine gültigen Items gefunden')
    })

    it('should reject malformed template-v2 (missing month-template)', async () => {
      const config = createSampleProject()
      const malformedTemplate = createMalformedTemplate('month-template')

      mockFetch('/* test css */') // CSS
      mockFetch(malformedTemplate)
      mockFetch(createSampleCSV())
      mockPapaParse()

      // Should handle gracefully without crashing
      await timelineEngine.loadProjectConfigObject(config)

      const viewer = document.getElementById('viewer').innerHTML
      // Either shows error message or handles gracefully
      expect(viewer).toBeTruthy()
    })

    it('should reject malformed template-v2 (missing lane-template)', async () => {
      const config = createSampleProject()
      const malformedTemplate = createMalformedTemplate('lane-template')

      mockFetch('/* test css */') // CSS
      mockFetch(malformedTemplate)
      mockFetch(createSampleCSV())
      mockPapaParse()

      // Should handle gracefully without crashing
      await timelineEngine.loadProjectConfigObject(config)

      const viewer = document.getElementById('viewer').innerHTML
      expect(viewer).toBeTruthy()
    })

    it('should reject malformed template-v2 (missing item-template)', async () => {
      const config = createSampleProject()
      const malformedTemplate = createMalformedTemplate('item-template')

      mockFetch('/* test css */') // CSS
      mockFetch(malformedTemplate)
      mockFetch(createSampleCSV())
      mockPapaParse()

      // Should handle gracefully without crashing
      await timelineEngine.loadProjectConfigObject(config)

      const viewer = document.getElementById('viewer').innerHTML
      expect(viewer).toBeTruthy()
    })

    it('should preserve template styling and definitions', async () => {
      const config = createSampleProject()

      // Read actual template-v2.svg to test real styling preservation
      const templateV2 = await fs.readFile('./example/template-v2.svg', 'utf-8')

      mockFetch('/* test css */') // CSS
      mockFetch(templateV2)
      mockFetch(createSampleCSV())
      mockPapaParse()

      await timelineEngine.loadProjectConfigObject(config)

      const svg = document.getElementById('viewer').innerHTML

      // Template elements are removed during generation (generator.js:202-205)
      // Instead verify that the template was successfully used to generate content
      expect(svg).toContain('<svg')
      expect(svg).toContain('viewBox')

      // Should have generated content from the template
      expect(svg).toContain('First Task') // Item from CSV
      expect(svg).toContain('Development') // Lane from CSV

      // Should have <defs> section (for gradients, filters, etc. but not template elements)
      expect(svg).toContain('<defs')
    })
  })

  describe('DOM Event Handling', () => {
    it('should handle project file upload', async () => {
      const config = createSampleProject()
      const projectInput = document.createElement('input')
      projectInput.id = 'projectInput'
      document.body.appendChild(projectInput)

      // Setup event handlers
      window.setupEventHandlers()

      // Mock file reading
      const mockFile = new File([JSON.stringify(config)], 'project.json', { type: 'application/json' })
      mockFile.text = vi.fn().mockResolvedValue(JSON.stringify(config))

      // Mock the fetch calls that loadProjectConfigObject will make
      mockFetch('/* test css */') // CSS
      mockFetch(createSampleTemplate())
      mockFetch(createSampleCSV())
      mockPapaParse()

      // Simulate file selection
      Object.defineProperty(projectInput, 'files', {
        value: [mockFile],
        writable: false
      })

      // Trigger the event
      const event = new Event('change')
      projectInput.dispatchEvent(event)

      // Wait for async operations
      await new Promise(resolve => setTimeout(resolve, 0))

      expect(document.getElementById('projectName').textContent).toBe('Test Project')
    })

    it('should handle CSV file upload', async () => {
      const config = createSampleProject()
      timelineEngine.config = config
      timelineEngine.template = createSampleTemplate() // Need template for generation

      const csvInput = document.createElement('input')
      csvInput.id = 'csvInput'
      document.body.appendChild(csvInput)

      // Setup event handlers
      window.setupEventHandlers()

      const csvContent = createSampleCSV()
      const mockFile = new File([csvContent], 'data.csv', { type: 'text/csv' })
      mockFile.text = vi.fn().mockResolvedValue(csvContent)

      global.Papa.parse.mockImplementation((text, options) => {
        options.complete({
          data: [{ ID: 'T-1', Title: 'Uploaded Task', Lane: 'Upload Lane', Due: '2025-01-15' }]
        })
      })

      Object.defineProperty(csvInput, 'files', {
        value: [mockFile],
        writable: false
      })

      const event = new Event('change')
      csvInput.dispatchEvent(event)

      await new Promise(resolve => setTimeout(resolve, 0))

      expect(timelineEngine.csvOverride).toBe(true)
      expect(document.getElementById('viewer').innerHTML).toContain('Uploaded Task')
    })
  })

  describe('SVG Export', () => {
    it('should hide IDs in external view for export', async () => {
      // Setup timeline with items
      const config = createSampleProject()
      mockFetch('/* test css */') // CSS
      mockFetch(createSampleTemplate())
      mockFetch(createSampleCSV())

      global.Papa.parse.mockImplementation((text, options) => {
        options.complete({
          data: [{ ID: 'T-1', Title: 'Export Task', Lane: 'Export Lane', Due: '2025-01-15' }]
        })
      })

      await timelineEngine.loadProjectConfigObject(config)

      // Mock the download functionality
      const mockCreateElement = vi.spyOn(document, 'createElement').mockImplementation((tagName) => {
        if (tagName === 'a') {
          return {
            href: '',
            download: '',
            click: vi.fn()
          }
        }
        return document.createElement(tagName)
      })

      global.URL.createObjectURL = vi.fn().mockReturnValue('blob:url')
      global.URL.revokeObjectURL = vi.fn()

      const downloadBtn = document.getElementById('downloadSvg')
      downloadBtn.click()

      // Verify that item-id elements would be hidden
      const svg = document.querySelector('#viewer svg')
      expect(svg).toBeTruthy()

      mockCreateElement.mockRestore()
    })

    it('should generate downloadable SVG with proper dimensions', async () => {
      const config = createSampleProject()
      mockFetch('/* test css */') // CSS
      mockFetch(createSampleTemplate())
      mockFetch(createSampleCSV())
      mockPapaParse()

      await timelineEngine.loadProjectConfigObject(config)

      const svg = document.querySelector('#viewer svg')

      // Verify SVG has width and height attributes
      expect(svg.getAttribute('width')).toBeTruthy()
      expect(svg.getAttribute('height')).toBeTruthy()
      expect(svg.getAttribute('viewBox')).toBeTruthy()

      // Verify viewBox matches dimensions
      const width = svg.getAttribute('width')
      const height = svg.getAttribute('height')
      const viewBox = svg.getAttribute('viewBox')
      expect(viewBox).toContain(width)
      expect(viewBox).toContain(height)
    })
  })
})
