/**
 * Tests for OCR Integration Utility
 *
 * Tests cover:
 * - mm to pixel conversion
 * - OCR data export format
 * - LocalStorage operations
 * - Canvas integration
 */

import {
  MM_TO_PX,
  A4_WIDTH_MM,
  A4_HEIGHT_MM,
  A4_WIDTH_PX,
  A4_HEIGHT_PX,
  mmToPixel,
  pixelToMm,
  getColumnColor,
  createTextProps,
  exportOCRData,
  saveOCRExportToStorage,
  loadLatestOCRExport,
  loadOCRExport,
  clearOCRExports,
  type OCRWord,
  type OCRExportData,
  type ColumnType,
} from './ocr-integration'

// Mock localStorage
const localStorageMock = (() => {
  let store: Record<string, string> = {}
  return {
    getItem: jest.fn((key: string) => store[key] || null),
    setItem: jest.fn((key: string, value: string) => {
      store[key] = value
    }),
    removeItem: jest.fn((key: string) => {
      delete store[key]
    }),
    clear: jest.fn(() => {
      store = {}
    }),
    keys: () => Object.keys(store),
  }
})()

Object.defineProperty(window, 'localStorage', { value: localStorageMock })

describe('Constants', () => {
  test('MM_TO_PX is correct for 96 DPI', () => {
    // 1 inch = 25.4mm, 96 DPI = 96 pixels per inch
    // 96 / 25.4 = 3.7795275591
    expect(MM_TO_PX).toBeCloseTo(3.7795275591, 8)
  })

  test('A4 dimensions in mm are correct', () => {
    expect(A4_WIDTH_MM).toBe(210)
    expect(A4_HEIGHT_MM).toBe(297)
  })

  test('A4 dimensions in pixels are calculated correctly', () => {
    expect(A4_WIDTH_PX).toBe(Math.round(210 * MM_TO_PX)) // ~794
    expect(A4_HEIGHT_PX).toBe(Math.round(297 * MM_TO_PX)) // ~1123
  })
})

describe('mmToPixel', () => {
  test('converts 0mm to 0px', () => {
    expect(mmToPixel(0)).toBe(0)
  })

  test('converts 1mm correctly', () => {
    expect(mmToPixel(1)).toBeCloseTo(3.7795275591, 8)
  })

  test('converts 100mm correctly', () => {
    expect(mmToPixel(100)).toBeCloseTo(377.95275591, 6)
  })

  test('converts A4 width correctly', () => {
    expect(mmToPixel(210)).toBeCloseTo(793.7, 1)
  })
})

describe('pixelToMm', () => {
  test('converts 0px to 0mm', () => {
    expect(pixelToMm(0)).toBe(0)
  })

  test('converts 100px correctly', () => {
    expect(pixelToMm(100)).toBeCloseTo(26.458, 2)
  })

  test('round-trip conversion is accurate', () => {
    const original = 50
    const pixels = mmToPixel(original)
    const backToMm = pixelToMm(pixels)
    expect(backToMm).toBeCloseTo(original, 8)
  })
})

describe('getColumnColor', () => {
  test('returns blue for english column', () => {
    expect(getColumnColor('english')).toBe('#1e40af')
  })

  test('returns green for german column', () => {
    expect(getColumnColor('german')).toBe('#166534')
  })

  test('returns purple for example column', () => {
    expect(getColumnColor('example')).toBe('#6b21a8')
  })

  test('returns gray for unknown column', () => {
    expect(getColumnColor('unknown')).toBe('#374151')
  })

  test('uses custom colors from options', () => {
    const options = { englishColor: '#ff0000' }
    expect(getColumnColor('english', options)).toBe('#ff0000')
  })
})

describe('createTextProps', () => {
  const mockWord: OCRWord = {
    text: 'house',
    x_mm: 21.0,
    y_mm: 44.55,
    width_mm: 52.5,
    height_mm: 8.91,
    column_type: 'english',
    logical_row: 0,
  }

  test('creates correct type', () => {
    const props = createTextProps(mockWord)
    expect(props.type).toBe('i-text')
  })

  test('converts mm to pixels for left position', () => {
    const props = createTextProps(mockWord)
    expect(props.left).toBeCloseTo(21.0 * MM_TO_PX, 2)
  })

  test('converts mm to pixels for top position', () => {
    const props = createTextProps(mockWord)
    expect(props.top).toBeCloseTo(44.55 * MM_TO_PX, 2)
  })

  test('applies offset correctly', () => {
    const props = createTextProps(mockWord, { offsetX: 5, offsetY: 10 })
    expect(props.left).toBeCloseTo((21.0 + 5) * MM_TO_PX, 2)
    expect(props.top).toBeCloseTo((44.55 + 10) * MM_TO_PX, 2)
  })

  test('sets fill color based on column type', () => {
    const props = createTextProps(mockWord)
    expect(props.fill).toBe('#1e40af') // English blue
  })

  test('includes OCR metadata', () => {
    const props = createTextProps(mockWord)
    expect(props.ocrMetadata).toBeDefined()
    expect((props.ocrMetadata as any).x_mm).toBe(21.0)
    expect((props.ocrMetadata as any).column_type).toBe('english')
    expect((props.ocrMetadata as any).logical_row).toBe(0)
  })

  test('uses custom font family', () => {
    const props = createTextProps(mockWord, { fontFamily: 'Times New Roman' })
    expect(props.fontFamily).toBe('Times New Roman')
  })

  test('uses custom font size', () => {
    const props = createTextProps(mockWord, { fontSize: 16 })
    expect(props.fontSize).toBe(16)
  })
})

describe('exportOCRData', () => {
  const mockGridData = {
    cells: [
      [
        {
          text: 'house',
          x_mm: 21.0,
          y_mm: 44.55,
          width_mm: 52.5,
          height_mm: 8.91,
          column_type: 'english' as ColumnType,
          logical_row: 0,
          status: 'recognized',
        },
        {
          text: 'Haus',
          x_mm: 80.0,
          y_mm: 44.55,
          width_mm: 40.0,
          height_mm: 8.91,
          column_type: 'german' as ColumnType,
          logical_row: 0,
          status: 'recognized',
        },
      ],
    ],
    detected_columns: [
      { column_type: 'english', x_start_mm: 20.0, x_end_mm: 73.5 },
      { column_type: 'german', x_start_mm: 74.0, x_end_mm: 140.0 },
    ],
    page_dimensions: {
      width_mm: 210,
      height_mm: 297,
      format: 'A4',
    },
  }

  test('creates correct version', () => {
    const result = exportOCRData(mockGridData, 'session-123', 1)
    expect(result.version).toBe('1.0')
  })

  test('sets correct source', () => {
    const result = exportOCRData(mockGridData, 'session-123', 1)
    expect(result.source).toBe('ocr-compare')
  })

  test('includes session ID and page number', () => {
    const result = exportOCRData(mockGridData, 'session-123', 1)
    expect(result.session_id).toBe('session-123')
    expect(result.page_number).toBe(1)
  })

  test('includes page dimensions', () => {
    const result = exportOCRData(mockGridData, 'session-123', 1)
    expect(result.page_dimensions.width_mm).toBe(210)
    expect(result.page_dimensions.height_mm).toBe(297)
    expect(result.page_dimensions.format).toBe('A4')
  })

  test('converts cells to words', () => {
    const result = exportOCRData(mockGridData, 'session-123', 1)
    expect(result.words).toHaveLength(2)
    expect(result.words[0].text).toBe('house')
    expect(result.words[0].column_type).toBe('english')
  })

  test('filters empty cells', () => {
    const dataWithEmpty = {
      ...mockGridData,
      cells: [
        [
          ...mockGridData.cells[0],
          { text: '', status: 'empty' }, // Empty cell
        ],
      ],
    }
    const result = exportOCRData(dataWithEmpty, 'session-123', 1)
    expect(result.words).toHaveLength(2) // Empty cell excluded
  })

  test('includes detected columns', () => {
    const result = exportOCRData(mockGridData, 'session-123', 1)
    expect(result.detected_columns).toHaveLength(2)
    expect(result.detected_columns[0].column_type).toBe('english')
  })

  test('sets exported_at timestamp', () => {
    const before = new Date().toISOString()
    const result = exportOCRData(mockGridData, 'session-123', 1)
    const after = new Date().toISOString()

    expect(result.exported_at >= before).toBe(true)
    expect(result.exported_at <= after).toBe(true)
  })
})

describe('localStorage operations', () => {
  beforeEach(() => {
    localStorageMock.clear()
  })

  const mockExportData: OCRExportData = {
    version: '1.0',
    source: 'ocr-compare',
    exported_at: '2026-02-08T12:00:00Z',
    session_id: 'session-123',
    page_number: 1,
    page_dimensions: {
      width_mm: 210,
      height_mm: 297,
      format: 'A4',
    },
    words: [
      {
        text: 'house',
        x_mm: 21.0,
        y_mm: 44.55,
        width_mm: 52.5,
        height_mm: 8.91,
        column_type: 'english',
        logical_row: 0,
      },
    ],
    detected_columns: [],
  }

  describe('saveOCRExportToStorage', () => {
    test('saves data to localStorage', () => {
      saveOCRExportToStorage(mockExportData)

      expect(localStorageMock.setItem).toHaveBeenCalledWith(
        'ocr_export_session-123_1',
        expect.any(String)
      )
    })

    test('sets latest export key', () => {
      saveOCRExportToStorage(mockExportData)

      expect(localStorageMock.setItem).toHaveBeenCalledWith(
        'ocr_export_latest',
        'ocr_export_session-123_1'
      )
    })
  })

  describe('loadLatestOCRExport', () => {
    test('returns null when no export exists', () => {
      const result = loadLatestOCRExport()
      expect(result).toBeNull()
    })

    test('loads latest export data', () => {
      // Manually set up the mock
      localStorageMock.setItem(
        'ocr_export_session-123_1',
        JSON.stringify(mockExportData)
      )
      localStorageMock.setItem('ocr_export_latest', 'ocr_export_session-123_1')

      // Reset the mock to return correct values
      localStorageMock.getItem.mockImplementation((key: string) => {
        if (key === 'ocr_export_latest') return 'ocr_export_session-123_1'
        if (key === 'ocr_export_session-123_1')
          return JSON.stringify(mockExportData)
        return null
      })

      const result = loadLatestOCRExport()
      expect(result).not.toBeNull()
      expect(result?.session_id).toBe('session-123')
    })
  })

  describe('loadOCRExport', () => {
    test('returns null for non-existent session', () => {
      const result = loadOCRExport('nonexistent', 1)
      expect(result).toBeNull()
    })

    test('loads specific export by session and page', () => {
      localStorageMock.getItem.mockImplementation((key: string) => {
        if (key === 'ocr_export_session-123_1')
          return JSON.stringify(mockExportData)
        return null
      })

      const result = loadOCRExport('session-123', 1)
      expect(result).not.toBeNull()
      expect(result?.page_number).toBe(1)
    })

    test('handles JSON parse errors gracefully', () => {
      localStorageMock.getItem.mockImplementation((key: string) => {
        if (key === 'ocr_export_session-123_1') return 'invalid json'
        return null
      })

      const result = loadOCRExport('session-123', 1)
      expect(result).toBeNull()
    })
  })

  describe('clearOCRExports', () => {
    test('removes all OCR export keys', () => {
      // Set up mock to return keys
      Object.defineProperty(localStorageMock, 'keys', {
        value: () => [
          'ocr_export_session-1_1',
          'ocr_export_session-2_1',
          'ocr_export_latest',
          'other_key',
        ],
      })

      // Mock Object.keys(localStorage)
      const originalKeys = Object.keys
      Object.keys = jest.fn((obj) => {
        if (obj === localStorage) {
          return [
            'ocr_export_session-1_1',
            'ocr_export_session-2_1',
            'ocr_export_latest',
            'other_key',
          ]
        }
        return originalKeys(obj)
      })

      clearOCRExports()

      expect(localStorageMock.removeItem).toHaveBeenCalledWith(
        'ocr_export_session-1_1'
      )
      expect(localStorageMock.removeItem).toHaveBeenCalledWith(
        'ocr_export_session-2_1'
      )
      expect(localStorageMock.removeItem).toHaveBeenCalledWith(
        'ocr_export_latest'
      )

      // Restore Object.keys
      Object.keys = originalKeys
    })
  })
})

describe('Edge Cases', () => {
  test('handles negative mm values', () => {
    const pixels = mmToPixel(-10)
    expect(pixels).toBeCloseTo(-37.795, 2)
  })

  test('handles very large mm values', () => {
    const pixels = mmToPixel(10000)
    expect(pixels).toBeCloseTo(37795.275591, 2)
  })

  test('handles word with missing optional fields', () => {
    const word: OCRWord = {
      text: 'test',
      x_mm: 0,
      y_mm: 0,
      width_mm: 10,
      height_mm: 5,
      column_type: 'unknown',
      logical_row: 0,
    }
    const props = createTextProps(word)
    expect(props).toBeDefined()
    expect(props.text).toBe('test')
  })

  test('handles empty words array in export', () => {
    const gridData = {
      cells: [],
      detected_columns: [],
      page_dimensions: { width_mm: 210, height_mm: 297, format: 'A4' },
    }
    const result = exportOCRData(gridData, 'session', 1)
    expect(result.words).toHaveLength(0)
  })
})
