/**
 * Data Integrity Tests
 * Validates that data is preserved and not corrupted during migration
 * Tests data loss prevention, foreign key relationships, and indexes
 */

import { PrismaClient } from '@prisma/client'
import {
  setupTestEnvironment,
  createTestPrismaClient,
  initializeTestDatabase,
  cleanupTestDatabase,
  createSampleNotes,
  verifyDataIntegrity
} from './setup'

describe('Data Integrity Tests', () => {
  let prisma: PrismaClient

  beforeAll(async () => {
    await setupTestEnvironment()
    prisma = createTestPrismaClient()
    await initializeTestDatabase(prisma)
  })

  afterAll(async () => {
    await cleanupTestDatabase(prisma)
  })

  describe('No Data Loss', () => {
    test('should preserve all notes after migration', async () => {
      // Create test notes
      const initialNotes = await createSampleNotes(prisma, 50)
      const initialCount = initialNotes.length
      
      // Query after migration
      const notesAfterMigration = await prisma.note.findMany()
      const finalCount = notesAfterMigration.length
      
      // Verify no data loss
      expect(finalCount).toBe(initialCount)
    })

    test('should preserve note titles', async () => {
      const testTitles = [
        'Important Meeting Notes',
        'Shopping List',
        'Project Ideas',
        'Recipe Collection',
        'Book Reviews'
      ]
      
      // Create notes with specific titles
      for (const title of testTitles) {
        await prisma.note.create({
          data: {
            title,
            content: `Content for ${title}`,
            userId: 'test-user-id'
          }
        })
      }
      
      // Verify all titles are preserved
      const notes = await prisma.note.findMany({
        where: { title: { in: testTitles } }
      })
      
      const preservedTitles = notes.map(n => n.title)
      for (const title of testTitles) {
        expect(preservedTitles).toContain(title)
      }
    })

    test('should preserve note content', async () => {
      const testContent = [
        'This is a simple text note',
        'Note with **markdown** formatting',
        'Note with [links](https://example.com)',
        'Note with numbers: 1, 2, 3, 4, 5',
        'Note with special characters: émojis 🎉 & çharacters'
      ]
      
      // Create notes with specific content
      for (const content of testContent) {
        await prisma.note.create({
          data: {
            title: `Content Test`,
            content,
            userId: 'test-user-id'
          }
        })
      }
      
      // Verify all content is preserved
      const notes = await prisma.note.findMany({
        where: { title: 'Content Test' }
      })
      
      const preservedContent = notes.map(n => n.content)
      for (const content of testContent) {
        expect(preservedContent).toContain(content)
      }
    })

    test('should preserve note metadata', async () => {
      // Create note with all metadata
      const note = await prisma.note.create({
        data: {
          title: 'Metadata Test Note',
          content: 'Test content',
          color: 'red',
          isPinned: true,
          isArchived: false,
          type: 'text',
          size: 'large',
          userId: 'test-user-id',
          order: 5
        }
      })
      
      // Verify metadata is preserved
      const retrieved = await prisma.note.findUnique({
        where: { id: note.id }
      })
      
      expect(retrieved?.color).toBe('red')
      expect(retrieved?.isPinned).toBe(true)
      expect(retrieved?.isArchived).toBe(false)
      expect(retrieved?.type).toBe('text')
      expect(retrieved?.size).toBe('large')
      expect(retrieved?.order).toBe(5)
    })
  })

  describe('No Data Corruption', () => {
    test('should preserve checkItems JSON structure', async () => {
      const checkItems = JSON.stringify([
        { text: 'Buy groceries', done: false },
        { text: 'Call dentist', done: true },
        { text: 'Finish report', done: false },
        { text: 'Schedule meeting', done: false }
      ])
      
      const note = await prisma.note.create({
        data: {
          title: 'Checklist Test Note',
          content: 'My checklist',
          checkItems,
          userId: 'test-user-id'
        }
      })
      
      // Verify checkItems are preserved and can be parsed
      const retrieved = await prisma.note.findUnique({
        where: { id: note.id }
      })
      
      expect(retrieved?.checkItems).toBeDefined()
      
      const parsedCheckItems = JSON.parse(retrieved?.checkItems || '[]')
      expect(parsedCheckItems.length).toBe(4)
      expect(parsedCheckItems[0].text).toBe('Buy groceries')
      expect(parsedCheckItems[0].done).toBe(false)
      expect(parsedCheckItems[1].done).toBe(true)
    })

    test('should preserve images JSON structure', async () => {
      const images = JSON.stringify([
        { url: 'image1.jpg', caption: 'First image' },
        { url: 'image2.jpg', caption: 'Second image' },
        { url: 'image3.jpg', caption: 'Third image' }
      ])
      
      const note = await prisma.note.create({
        data: {
          title: 'Images Test Note',
          content: 'Note with images',
          images,
          userId: 'test-user-id'
        }
      })
      
      // Verify images are preserved and can be parsed
      const retrieved = await prisma.note.findUnique({
        where: { id: note.id }
      })
      
      expect(retrieved?.images).toBeDefined()
      
      const parsedImages = JSON.parse(retrieved?.images || '[]')
      expect(parsedImages.length).toBe(3)
      expect(parsedImages[0].url).toBe('image1.jpg')
      expect(parsedImages[0].caption).toBe('First image')
    })

    test('should preserve labels JSON structure', async () => {
      const labels = JSON.stringify(['work', 'important', 'project'])
      
      const note = await prisma.note.create({
        data: {
          title: 'Labels Test Note',
          content: 'Note with labels',
          labels,
          userId: 'test-user-id'
        }
      })
      
      // Verify labels are preserved and can be parsed
      const retrieved = await prisma.note.findUnique({
        where: { id: note.id }
      })
      
      expect(retrieved?.labels).toBeDefined()
      
      const parsedLabels = JSON.parse(retrieved?.labels || '[]')
      expect(parsedLabels.length).toBe(3)
      expect(parsedLabels).toContain('work')
      expect(parsedLabels).toContain('important')
      expect(parsedLabels).toContain('project')
    })

    test('should preserve embedding JSON structure', async () => {
      const embedding = JSON.stringify({
        vector: [0.1, 0.2, 0.3, 0.4, 0.5],
        model: 'text-embedding-ada-002',
        timestamp: new Date().toISOString()
      })
      
      const note = await prisma.note.create({
        data: {
          title: 'Embedding Test Note',
          content: 'Note with embedding',
          embedding,
          userId: 'test-user-id'
        }
      })
      
      // Verify embedding is preserved and can be parsed
      const retrieved = await prisma.note.findUnique({
        where: { id: note.id }
      })
      
      expect(retrieved?.embedding).toBeDefined()
      
      const parsedEmbedding = JSON.parse(retrieved?.embedding || '{}')
      expect(parsedEmbedding.vector).toEqual([0.1, 0.2, 0.3, 0.4, 0.5])
      expect(parsedEmbedding.model).toBe('text-embedding-ada-002')
    })

    test('should preserve links JSON structure', async () => {
      const links = JSON.stringify([
        { url: 'https://example.com', title: 'Example' },
        { url: 'https://docs.example.com', title: 'Documentation' }
      ])
      
      const note = await prisma.note.create({
        data: {
          title: 'Links Test Note',
          content: 'Note with links',
          links,
          userId: 'test-user-id'
        }
      })
      
      // Verify links are preserved and can be parsed
      const retrieved = await prisma.note.findUnique({
        where: { id: note.id }
      })
      
      expect(retrieved?.links).toBeDefined()
      
      const parsedLinks = JSON.parse(retrieved?.links || '[]')
      expect(parsedLinks.length).toBe(2)
      expect(parsedLinks[0].url).toBe('https://example.com')
      expect(parsedLinks[0].title).toBe('Example')
    })
  })

  describe('Foreign Key Relationships', () => {
    test('should maintain Note to User relationship', async () => {
      const user = await prisma.user.create({
        data: {
          email: 'fk-integrity@test.com',
          name: 'FK Integrity User'
        }
      })
      
      const note = await prisma.note.create({
        data: {
          title: 'User Integrity Note',
          content: 'Test content',
          userId: user.id
        }
      })
      
      // Verify relationship is maintained
      expect(note.userId).toBe(user.id)
      
      // Query user's notes
      const userNotes = await prisma.note.findMany({
        where: { userId: user.id }
      })
      
      expect(userNotes.length).toBeGreaterThan(0)
      expect(userNotes.some(n => n.id === note.id)).toBe(true)
    })

    test('should maintain Note to Notebook relationship', async () => {
      const notebook = await prisma.notebook.create({
        data: {
          name: 'Integrity Notebook',
          order: 0,
          userId: 'test-user-id'
        }
      })
      
      const note = await prisma.note.create({
        data: {
          title: 'Notebook Integrity Note',
          content: 'Test content',
          userId: 'test-user-id',
          notebookId: notebook.id
        }
      })
      
      // Verify relationship is maintained
      expect(note.notebookId).toBe(notebook.id)
      
      // Query notebook's notes
      const notebookNotes = await prisma.note.findMany({
        where: { notebookId: notebook.id }
      })
      
      expect(notebookNotes.length).toBeGreaterThan(0)
      expect(notebookNotes.some(n => n.id === note.id)).toBe(true)
    })

    test('should maintain AiFeedback to Note relationship', async () => {
      const note = await prisma.note.create({
        data: {
          title: 'Feedback Integrity Note',
          content: 'Test content',
          userId: 'test-user-id'
        }
      })
      
      const feedback = await prisma.aiFeedback.create({
        data: {
          noteId: note.id,
          userId: 'test-user-id',
          feedbackType: 'thumbs_up',
          feature: 'title_suggestion',
          originalContent: 'Test feedback'
        }
      })
      
      // Verify relationship is maintained
      expect(feedback.noteId).toBe(note.id)
      
      // Query note's feedback
      const noteFeedback = await prisma.aiFeedback.findMany({
        where: { noteId: note.id }
      })
      
      expect(noteFeedback.length).toBeGreaterThan(0)
      expect(noteFeedback.some(f => f.id === feedback.id)).toBe(true)
    })

    test('should maintain AiFeedback to User relationship', async () => {
      const user = await prisma.user.create({
        data: {
          email: 'feedback-user@test.com',
          name: 'Feedback User'
        }
      })
      
      const note = await prisma.note.create({
        data: {
          title: 'User Feedback Note',
          content: 'Test content',
          userId: user.id
        }
      })
      
      const feedback = await prisma.aiFeedback.create({
        data: {
          noteId: note.id,
          userId: user.id,
          feedbackType: 'thumbs_up',
          feature: 'title_suggestion',
          originalContent: 'Test feedback'
        }
      })
      
      // Verify relationship is maintained
      expect(feedback.userId).toBe(user.id)
      
      // Query user's feedback
      const userFeedback = await prisma.aiFeedback.findMany({
        where: { userId: user.id }
      })
      
      expect(userFeedback.length).toBeGreaterThan(0)
      expect(userFeedback.some(f => f.id === feedback.id)).toBe(true)
    })

    test('should maintain cascade delete correctly', async () => {
      // Create a note with feedback
      const note = await prisma.note.create({
        data: {
          title: 'Cascade Delete Note',
          content: 'Test content',
          userId: 'test-user-id'
        }
      })
      
      const feedback = await prisma.aiFeedback.create({
        data: {
          noteId: note.id,
          userId: 'test-user-id',
          feedbackType: 'thumbs_up',
          feature: 'title_suggestion',
          originalContent: 'Test feedback'
        }
      })
      
      // Verify feedback exists
      const feedbacksBefore = await prisma.aiFeedback.findMany({
        where: { noteId: note.id }
      })
      expect(feedbacksBefore.length).toBe(1)
      
      // Delete note
      await prisma.note.delete({
        where: { id: note.id }
      })
      
      // Verify feedback is cascade deleted
      const feedbacksAfter = await prisma.aiFeedback.findMany({
        where: { noteId: note.id }
      })
      expect(feedbacksAfter.length).toBe(0)
    })
  })

  describe('Index Integrity', () => {
    test('should maintain index on Note.isPinned', async () => {
      // Create notes with various pinned states
      await prisma.note.createMany({
        data: [
          { title: 'Pinned 1', content: 'Content 1', userId: 'test-user-id', isPinned: true },
          { title: 'Not Pinned 1', content: 'Content 2', userId: 'test-user-id', isPinned: false },
          { title: 'Pinned 2', content: 'Content 3', userId: 'test-user-id', isPinned: true },
          { title: 'Not Pinned 2', content: 'Content 4', userId: 'test-user-id', isPinned: false }
        ]
      })
      
      // Query by isPinned (should use index)
      const pinnedNotes = await prisma.note.findMany({
        where: { isPinned: true }
      })
      
      expect(pinnedNotes.length).toBe(2)
      expect(pinnedNotes.every(n => n.isPinned === true)).toBe(true)
    })

    test('should maintain index on Note.order', async () => {
      // Create notes with specific order
      await prisma.note.createMany({
        data: [
          { title: 'Order 0', content: 'Content 0', userId: 'test-user-id', order: 0 },
          { title: 'Order 1', content: 'Content 1', userId: 'test-user-id', order: 1 },
          { title: 'Order 2', content: 'Content 2', userId: 'test-user-id', order: 2 },
          { title: 'Order 3', content: 'Content 3', userId: 'test-user-id', order: 3 }
        ]
      })
      
      // Query ordered by order (should use index)
      const orderedNotes = await prisma.note.findMany({
        orderBy: { order: 'asc' }
      })
      
      expect(orderedNotes[0].order).toBe(0)
      expect(orderedNotes[1].order).toBe(1)
      expect(orderedNotes[2].order).toBe(2)
      expect(orderedNotes[3].order).toBe(3)
    })

    test('should maintain index on AiFeedback.noteId', async () => {
      const note = await prisma.note.create({
        data: {
          title: 'Index Feedback Note',
          content: 'Test content',
          userId: 'test-user-id'
        }
      })
      
      // Create multiple feedback entries
      await prisma.aiFeedback.createMany({
        data: [
          { noteId: note.id, userId: 'test-user-id', feedbackType: 'thumbs_up', feature: 'title_suggestion', originalContent: 'Feedback 1' },
          { noteId: note.id, userId: 'test-user-id', feedbackType: 'thumbs_down', feature: 'semantic_search', originalContent: 'Feedback 2' },
          { noteId: note.id, userId: 'test-user-id', feedbackType: 'thumbs_up', feature: 'paragraph_refactor', originalContent: 'Feedback 3' }
        ]
      })
      
      // Query by noteId (should use index)
      const feedbacks = await prisma.aiFeedback.findMany({
        where: { noteId: note.id }
      })
      
      expect(feedbacks.length).toBe(3)
    })

    test('should maintain index on AiFeedback.userId', async () => {
      const note = await prisma.note.create({
        data: {
          title: 'User Index Feedback Note',
          content: 'Test content',
          userId: 'test-user-id'
        }
      })
      
      // Create multiple feedback entries for same user
      await prisma.aiFeedback.createMany({
        data: [
          { noteId: note.id, userId: 'test-user-id', feedbackType: 'thumbs_up', feature: 'title_suggestion', originalContent: 'Feedback 1' },
          { noteId: note.id, userId: 'test-user-id', feedbackType: 'thumbs_down', feature: 'semantic_search', originalContent: 'Feedback 2' }
        ]
      })
      
      // Query by userId (should use index)
      const userFeedbacks = await prisma.aiFeedback.findMany({
        where: { userId: 'test-user-id' }
      })
      
      expect(userFeedbacks.length).toBeGreaterThanOrEqual(2)
    })

    test('should maintain index on AiFeedback.feature', async () => {
      const note1 = await prisma.note.create({
        data: {
          title: 'Feature Index Note 1',
          content: 'Test content 1',
          userId: 'test-user-id'
        }
      })
      
      const note2 = await prisma.note.create({
        data: {
          title: 'Feature Index Note 2',
          content: 'Test content 2',
          userId: 'test-user-id'
        }
      })
      
      // Create feedback with same feature
      await prisma.aiFeedback.createMany({
        data: [
          { noteId: note1.id, userId: 'test-user-id', feedbackType: 'thumbs_up', feature: 'title_suggestion', originalContent: 'Feedback 1' },
          { noteId: note2.id, userId: 'test-user-id', feedbackType: 'thumbs_up', feature: 'title_suggestion', originalContent: 'Feedback 2' }
        ]
      })
      
      // Query by feature (should use index)
      const titleFeedbacks = await prisma.aiFeedback.findMany({
        where: { feature: 'title_suggestion' }
      })
      
      expect(titleFeedbacks.length).toBeGreaterThanOrEqual(2)
    })
  })

  describe('AI Fields Integrity', () => {
    test('should preserve AI fields correctly', async () => {
      const note = await prisma.note.create({
        data: {
          title: 'AI Fields Integrity Note',
          content: 'Test content',
          userId: 'test-user-id',
          autoGenerated: true,
          aiProvider: 'openai',
          aiConfidence: 95,
          language: 'en',
          languageConfidence: 0.98,
          lastAiAnalysis: new Date()
        }
      })
      
      // Verify all AI fields are preserved
      const retrieved = await prisma.note.findUnique({
        where: { id: note.id }
      })
      
      expect(retrieved?.autoGenerated).toBe(true)
      expect(retrieved?.aiProvider).toBe('openai')
      expect(retrieved?.aiConfidence).toBe(95)
      expect(retrieved?.language).toBe('en')
      expect(retrieved?.languageConfidence).toBeCloseTo(0.98)
      expect(retrieved?.lastAiAnalysis).toBeDefined()
    })

    test('should preserve null AI fields correctly', async () => {
      const note = await prisma.note.create({
        data: {
          title: 'Null AI Fields Note',
          content: 'Test content',
          userId: 'test-user-id'
          // All AI fields are null by default
        }
      })
      
      // Verify all AI fields are null
      const retrieved = await prisma.note.findUnique({
        where: { id: note.id }
      })
      
      expect(retrieved?.autoGenerated).toBeNull()
      expect(retrieved?.aiProvider).toBeNull()
      expect(retrieved?.aiConfidence).toBeNull()
      expect(retrieved?.language).toBeNull()
      expect(retrieved?.languageConfidence).toBeNull()
      expect(retrieved?.lastAiAnalysis).toBeNull()
    })
  })

  describe('Batch Operations Integrity', () => {
    test('should preserve data integrity during batch insert', async () => {
      const notesData = Array.from({ length: 50 }, (_, i) => ({
        title: `Batch Integrity Note ${i}`,
        content: `Content ${i}`,
        userId: 'test-user-id',
        order: i,
        isPinned: i % 2 === 0
      }))
      
      const result = await prisma.note.createMany({
        data: notesData
      })
      
      expect(result.count).toBe(50)
      
      // Verify all notes are created correctly
      const notes = await prisma.note.findMany({
        where: { title: { contains: 'Batch Integrity Note' } }
      })
      
      expect(notes.length).toBe(50)
      
      // Verify data integrity
      for (const note of notes) {
        expect(note.content).toBeDefined()
        expect(note.userId).toBe('test-user-id')
      }
    })
  })

  describe('Data Type Integrity', () => {
    test('should preserve boolean values correctly', async () => {
      const note = await prisma.note.create({
        data: {
          title: 'Boolean Test Note',
          content: 'Test content',
          userId: 'test-user-id',
          isPinned: true,
          isArchived: false
        }
      })
      
      const retrieved = await prisma.note.findUnique({
        where: { id: note.id }
      })
      
      expect(retrieved?.isPinned).toBe(true)
      expect(retrieved?.isArchived).toBe(false)
    })

    test('should preserve numeric values correctly', async () => {
      const note = await prisma.note.create({
        data: {
          title: 'Numeric Test Note',
          content: 'Test content',
          userId: 'test-user-id',
          order: 42,
          aiConfidence: 87,
          languageConfidence: 0.95
        }
      })
      
      const retrieved = await prisma.note.findUnique({
        where: { id: note.id }
      })
      
      expect(retrieved?.order).toBe(42)
      expect(retrieved?.aiConfidence).toBe(87)
      expect(retrieved?.languageConfidence).toBeCloseTo(0.95)
    })

    test('should preserve date values correctly', async () => {
      const testDate = new Date('2024-01-15T10:30:00Z')
      
      const note = await prisma.note.create({
        data: {
          title: 'Date Test Note',
          content: 'Test content',
          userId: 'test-user-id',
          reminder: testDate,
          lastAiAnalysis: testDate
        }
      })
      
      const retrieved = await prisma.note.findUnique({
        where: { id: note.id }
      })
      
      expect(retrieved?.reminder).toBeDefined()
      expect(retrieved?.lastAiAnalysis).toBeDefined()
    })
  })
})
