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

// Import the engine by loading it as text and evaluating
// This is needed because engine.js creates a global object
const fs = await import('fs/promises')
const engineCode = await fs.readFile('./engine.js', 'utf-8')

describe('Timeline Engine', () => {
  let timelineEngine

  beforeEach(async () => {
    setupBasicDOM()

    // Reset global window object
    global.window = global
    global.timelineGenerator = { generate: vi.fn() }

    // Execute engine code to create timelineEngine
    eval(engineCode)
    timelineEngine = global.window.timelineEngine
  })

  describe('parseDate', () => {
    it('should parse YYYY-MM-DD format', () => {
      const result = timelineEngine.parseDate('2025-12-15')
      expect(result).toEqual(new Date(2025, 11, 15))
    })

    it('should parse YYYY/MM/DD format', () => {
      const result = timelineEngine.parseDate('2025/12/15')
      expect(result).toEqual(new Date(2025, 11, 15))
    })

    it('should parse DD.MM.YYYY format', () => {
      const result = timelineEngine.parseDate('15.12.2025')
      expect(result).toEqual(new Date(2025, 11, 15))
    })

    it('should return null for invalid dates', () => {
      expect(timelineEngine.parseDate('invalid')).toBeNull()
      expect(timelineEngine.parseDate('')).toBeNull()
      expect(timelineEngine.parseDate(null)).toBeNull()
    })

    it('should handle single digit months and days', () => {
      const result = timelineEngine.parseDate('2025-1-5')
      expect(result).toEqual(new Date(2025, 0, 5))
    })
  })

  describe('loadProjectConfigObject', () => {
    it('should load project configuration and update DOM', async () => {
      const config = createSampleProject()

      mockFetch('<svg></svg>')
      mockFetch(createSampleCSV())
      mockPapaParse()

      await timelineEngine.loadProjectConfigObject(config)

      expect(timelineEngine.config).toEqual(config)
      expectElementToHaveText('#projectName', 'Test Project')
      expectElementToHaveText('#projectSubtitle', 'A test project for unit testing')
    })

    it('should handle missing stylesheet gracefully', async () => {
      const config = { name: 'Test', description: 'Test desc' }

      await timelineEngine.loadProjectConfigObject(config)

      expect(timelineEngine.config).toEqual(config)
    })

    it('should not override CSS when cssOverride is true', async () => {
      timelineEngine.cssOverride = true
      const config = createSampleProject()

      // Still need to mock SVG template and CSV fetches
      mockFetch('<svg></svg>')
      mockFetch(createSampleCSV())
      mockPapaParse()

      await timelineEngine.loadProjectConfigObject(config)

      // Should not load the stylesheet from config
      const href = document.getElementById('dynamicCss').href
      expect(href).not.toContain(config.stylesheet)
    })

    it('should not load CSV when csvOverride is true', async () => {
      timelineEngine.csvOverride = true
      const config = createSampleProject()
      const processCsvSpy = vi.spyOn(timelineEngine, 'processCsv')

      // Still need to mock SVG template fetch
      mockFetch('<svg></svg>')

      await timelineEngine.loadProjectConfigObject(config)

      expect(processCsvSpy).not.toHaveBeenCalled()
    })
  })

  describe('processCsv', () => {
    beforeEach(() => {
      timelineEngine.config = createSampleProject()
      global.Papa.parse.mockImplementation((text, options) => {
        const mockData = [
          { ID: 'T-1', Title: 'Task 1', Lane: 'Dev', Due: '2025-01-15' },
          { ID: 'T-2', Title: 'Task 2', Lane: 'Test', Due: '2025-02-20' },
          { ID: '', Title: '', Lane: '', Due: '' } // Empty row should be filtered
        ]
        options.complete({ data: mockData })
      })
    })

    it('should process CSV and generate timeline', () => {
      global.timelineGenerator.generate.mockReturnValue('<svg>test</svg>')

      timelineEngine.processCsv(createSampleCSV())

      expect(global.Papa.parse).toHaveBeenCalled()
      expect(global.timelineGenerator.generate).toHaveBeenCalled()
      expect(document.getElementById('viewer').innerHTML).toBe('<svg>test</svg>')
    })

    it('should filter out items without title or due date', () => {
      global.timelineGenerator.generate.mockImplementation((items) => {
        expect(items).toHaveLength(2) // Empty row should be filtered out
        return '<svg>test</svg>'
      })

      timelineEngine.processCsv(createSampleCSV())
    })

    it('should handle missing config gracefully', () => {
      timelineEngine.config = null
      const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})

      timelineEngine.processCsv(createSampleCSV())

      expect(consoleSpy).toHaveBeenCalledWith('No config or fieldMapping found.')
      consoleSpy.mockRestore()
    })

    it('should show message when no valid items found', () => {
      global.Papa.parse.mockImplementation((text, options) => {
        options.complete({ data: [] })
      })

      timelineEngine.processCsv('')

      expect(document.getElementById('viewer').innerHTML).toContain('Keine gültigen Items gefunden')
    })

    it('should enable download button after successful generation', () => {
      global.timelineGenerator.generate.mockReturnValue('<svg>test</svg>')
      const downloadBtn = document.getElementById('downloadSvg')

      timelineEngine.processCsv(createSampleCSV())

      expect(downloadBtn.disabled).toBe(false)
      expect(downloadBtn.style.opacity).toBe('1')
    })
  })

  describe('autoLoadDefaultProject', () => {
    it('should try binect/project.json first, then my-project, then example', async () => {
      // First call (binect) fails
      global.fetch.mockResolvedValueOnce({ ok: false, status: 404, statusText: 'Not Found' })
      // Second call (my-project) fails
      global.fetch.mockResolvedValueOnce({ ok: false, status: 404, statusText: 'Not Found' })
      // Third call (example) succeeds
      mockFetch(createSampleProject())
      mockFetch('<svg></svg>')
      mockFetch(createSampleCSV())

      await timelineEngine.autoLoadDefaultProject()

      expect(global.fetch).toHaveBeenCalledWith('binect/project.json')
      expect(global.fetch).toHaveBeenCalledWith('my-project/project.json')
      expect(global.fetch).toHaveBeenCalledWith('example/project.json')
    })

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

      // Should not throw
      await expect(timelineEngine.autoLoadDefaultProject()).resolves.toBeUndefined()
    })
  })
})