/**
 * Schema Migration Tests
 * Validates that all schema migrations (SQL migrations) work correctly
 * Tests database structure, indexes, and relationships
 */

import { PrismaClient } from '@prisma/client'
import {
  createTestPrismaClient,
  initializeTestDatabase,
  cleanupTestDatabase,
  verifyTableExists,
  verifyIndexExists,
  verifyColumnExists,
  getTableSchema
} from './setup'

describe('Schema Migration Tests', () => {
  let prisma: PrismaClient

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

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

  describe('Core Table Existence', () => {
    test('should have User table', async () => {
      const exists = await verifyTableExists(prisma, 'User')
      expect(exists).toBe(true)
    })

    test('should have Note table', async () => {
      const exists = await verifyTableExists(prisma, 'Note')
      expect(exists).toBe(true)
    })

    test('should have Notebook table', async () => {
      const exists = await verifyTableExists(prisma, 'Notebook')
      expect(exists).toBe(true)
    })

    test('should have Label table', async () => {
      const exists = await verifyTableExists(prisma, 'Label')
      expect(exists).toBe(true)
    })

    test('should have Account table', async () => {
      const exists = await verifyTableExists(prisma, 'Account')
      expect(exists).toBe(true)
    })

    test('should have Session table', async () => {
      const exists = await verifyTableExists(prisma, 'Session')
      expect(exists).toBe(true)
    })
  })

  describe('AI Feature Tables', () => {
    test('should have AiFeedback table', async () => {
      const exists = await verifyTableExists(prisma, 'AiFeedback')
      expect(exists).toBe(true)
    })

    test('should have MemoryEchoInsight table', async () => {
      const exists = await verifyTableExists(prisma, 'MemoryEchoInsight')
      expect(exists).toBe(true)
    })

    test('should have UserAISettings table', async () => {
      const exists = await verifyTableExists(prisma, 'UserAISettings')
      expect(exists).toBe(true)
    })
  })

  describe('Note Table AI Fields Migration', () => {
    test('should have autoGenerated column', async () => {
      const exists = await verifyColumnExists(prisma, 'Note', 'autoGenerated')
      expect(exists).toBe(true)
    })

    test('should have aiProvider column', async () => {
      const exists = await verifyColumnExists(prisma, 'Note', 'aiProvider')
      expect(exists).toBe(true)
    })

    test('should have aiConfidence column', async () => {
      const exists = await verifyColumnExists(prisma, 'Note', 'aiConfidence')
      expect(exists).toBe(true)
    })

    test('should have language column', async () => {
      const exists = await verifyColumnExists(prisma, 'Note', 'language')
      expect(exists).toBe(true)
    })

    test('should have languageConfidence column', async () => {
      const exists = await verifyColumnExists(prisma, 'Note', 'languageConfidence')
      expect(exists).toBe(true)
    })

    test('should have lastAiAnalysis column', async () => {
      const exists = await verifyColumnExists(prisma, 'Note', 'lastAiAnalysis')
      expect(exists).toBe(true)
    })
  })

  describe('AiFeedback Table Structure', () => {
    test('should have noteId column', async () => {
      const exists = await verifyColumnExists(prisma, 'AiFeedback', 'noteId')
      expect(exists).toBe(true)
    })

    test('should have userId column', async () => {
      const exists = await verifyColumnExists(prisma, 'AiFeedback', 'userId')
      expect(exists).toBe(true)
    })

    test('should have feedbackType column', async () => {
      const exists = await verifyColumnExists(prisma, 'AiFeedback', 'feedbackType')
      expect(exists).toBe(true)
    })

    test('should have feature column', async () => {
      const exists = await verifyColumnExists(prisma, 'AiFeedback', 'feature')
      expect(exists).toBe(true)
    })

    test('should have originalContent column', async () => {
      const exists = await verifyColumnExists(prisma, 'AiFeedback', 'originalContent')
      expect(exists).toBe(true)
    })

    test('should have correctedContent column', async () => {
      const exists = await verifyColumnExists(prisma, 'AiFeedback', 'correctedContent')
      expect(exists).toBe(true)
    })

    test('should have metadata column', async () => {
      const exists = await verifyColumnExists(prisma, 'AiFeedback', 'metadata')
      expect(exists).toBe(true)
    })

    test('should have createdAt column', async () => {
      const exists = await verifyColumnExists(prisma, 'AiFeedback', 'createdAt')
      expect(exists).toBe(true)
    })
  })

  describe('MemoryEchoInsight Table Structure', () => {
    test('should have userId column', async () => {
      const exists = await verifyColumnExists(prisma, 'MemoryEchoInsight', 'userId')
      expect(exists).toBe(true)
    })

    test('should have note1Id column', async () => {
      const exists = await verifyColumnExists(prisma, 'MemoryEchoInsight', 'note1Id')
      expect(exists).toBe(true)
    })

    test('should have note2Id column', async () => {
      const exists = await verifyColumnExists(prisma, 'MemoryEchoInsight', 'note2Id')
      expect(exists).toBe(true)
    })

    test('should have similarityScore column', async () => {
      const exists = await verifyColumnExists(prisma, 'MemoryEchoInsight', 'similarityScore')
      expect(exists).toBe(true)
    })

    test('should have insight column', async () => {
      const exists = await verifyColumnExists(prisma, 'MemoryEchoInsight', 'insight')
      expect(exists).toBe(true)
    })

    test('should have insightDate column', async () => {
      const exists = await verifyColumnExists(prisma, 'MemoryEchoInsight', 'insightDate')
      expect(exists).toBe(true)
    })

    test('should have viewed column', async () => {
      const exists = await verifyColumnExists(prisma, 'MemoryEchoInsight', 'viewed')
      expect(exists).toBe(true)
    })

    test('should have feedback column', async () => {
      const exists = await verifyColumnExists(prisma, 'MemoryEchoInsight', 'feedback')
      expect(exists).toBe(true)
    })

    test('should have dismissed column', async () => {
      const exists = await verifyColumnExists(prisma, 'MemoryEchoInsight', 'dismissed')
      expect(exists).toBe(true)
    })
  })

  describe('UserAISettings Table Structure', () => {
    test('should have userId column', async () => {
      const exists = await verifyColumnExists(prisma, 'UserAISettings', 'userId')
      expect(exists).toBe(true)
    })

    test('should have titleSuggestions column', async () => {
      const exists = await verifyColumnExists(prisma, 'UserAISettings', 'titleSuggestions')
      expect(exists).toBe(true)
    })

    test('should have semanticSearch column', async () => {
      const exists = await verifyColumnExists(prisma, 'UserAISettings', 'semanticSearch')
      expect(exists).toBe(true)
    })

    test('should have paragraphRefactor column', async () => {
      const exists = await verifyColumnExists(prisma, 'UserAISettings', 'paragraphRefactor')
      expect(exists).toBe(true)
    })

    test('should have memoryEcho column', async () => {
      const exists = await verifyColumnExists(prisma, 'UserAISettings', 'memoryEcho')
      expect(exists).toBe(true)
    })

    test('should have memoryEchoFrequency column', async () => {
      const exists = await verifyColumnExists(prisma, 'UserAISettings', 'memoryEchoFrequency')
      expect(exists).toBe(true)
    })

    test('should have aiProvider column', async () => {
      const exists = await verifyColumnExists(prisma, 'UserAISettings', 'aiProvider')
      expect(exists).toBe(true)
    })

    test('should have preferredLanguage column', async () => {
      const exists = await verifyColumnExists(prisma, 'UserAISettings', 'preferredLanguage')
      expect(exists).toBe(true)
    })

    test('should have fontSize column', async () => {
      const exists = await verifyColumnExists(prisma, 'UserAISettings', 'fontSize')
      expect(exists).toBe(true)
    })

    test('should have demoMode column', async () => {
      const exists = await verifyColumnExists(prisma, 'UserAISettings', 'demoMode')
      expect(exists).toBe(true)
    })

    test('should have showRecentNotes column', async () => {
      const exists = await verifyColumnExists(prisma, 'UserAISettings', 'showRecentNotes')
      expect(exists).toBe(true)
    })

    test('should have emailNotifications column', async () => {
      const exists = await verifyColumnExists(prisma, 'UserAISettings', 'emailNotifications')
      expect(exists).toBe(true)
    })

    test('should have desktopNotifications column', async () => {
      const exists = await verifyColumnExists(prisma, 'UserAISettings', 'desktopNotifications')
      expect(exists).toBe(true)
    })

    test('should have anonymousAnalytics column', async () => {
      const exists = await verifyColumnExists(prisma, 'UserAISettings', 'anonymousAnalytics')
      expect(exists).toBe(true)
    })
  })

  describe('Index Creation', () => {
    test('should have indexes on AiFeedback.noteId', async () => {
      const exists = await verifyIndexExists(prisma, 'AiFeedback', 'AiFeedback_noteId_idx')
      expect(exists).toBe(true)
    })

    test('should have indexes on AiFeedback.userId', async () => {
      const exists = await verifyIndexExists(prisma, 'AiFeedback', 'AiFeedback_userId_idx')
      expect(exists).toBe(true)
    })

    test('should have indexes on AiFeedback.feature', async () => {
      const exists = await verifyIndexExists(prisma, 'AiFeedback', 'AiFeedback_feature_idx')
      expect(exists).toBe(true)
    })

    test('should have indexes on AiFeedback.createdAt', async () => {
      const exists = await verifyIndexExists(prisma, 'AiFeedback', 'AiFeedback_createdAt_idx')
      expect(exists).toBe(true)
    })

    test('should have indexes on Note table', async () => {
      // Note table should have indexes on various columns
      const schema = await getTableSchema(prisma, 'Note')
      expect(schema).toBeDefined()
    })
  })

  describe('Foreign Key Relationships', () => {
    test('should maintain Note to AiFeedback relationship', async () => {
      // Create a test note
      const note = await prisma.note.create({
        data: {
          title: 'Test FK Note',
          content: 'Test content',
          userId: 'test-user-id'
        }
      })

      // Create feedback linked to the note
      const feedback = await prisma.aiFeedback.create({
        data: {
          noteId: note.id,
          userId: 'test-user-id',
          feedbackType: 'thumbs_up',
          feature: 'title_suggestion',
          originalContent: 'Test feedback'
        }
      })

      expect(feedback.noteId).toBe(note.id)
    })

    test('should maintain User to AiFeedback relationship', async () => {
      // Create a test note
      const note = await prisma.note.create({
        data: {
          title: 'Test User FK Note',
          content: 'Test content',
          userId: 'test-user-id'
        }
      })

      // Create feedback linked to user
      const feedback = await prisma.aiFeedback.create({
        data: {
          noteId: note.id,
          userId: 'test-user-id',
          feedbackType: 'thumbs_down',
          feature: 'semantic_search',
          originalContent: 'Test feedback'
        }
      })

      expect(feedback.userId).toBe('test-user-id')
    })

    test('should cascade delete AiFeedback when Note is deleted', async () => {
      // Create a note with feedback
      const note = await prisma.note.create({
        data: {
          title: 'Test Cascade Note',
          content: 'Test content',
          userId: 'test-user-id'
        }
      })

      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 the 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)
    })

    test('should maintain Note to Notebook relationship', async () => {
      // Create a notebook
      const notebook = await prisma.notebook.create({
        data: {
          name: 'Test Notebook',
          order: 0,
          userId: 'test-user-id'
        }
      })

      // Create a note in the notebook
      const note = await prisma.note.create({
        data: {
          title: 'Test Notebook Note',
          content: 'Test content',
          userId: 'test-user-id',
          notebookId: notebook.id
        }
      })

      expect(note.notebookId).toBe(notebook.id)
    })
  })

  describe('Unique Constraints', () => {
    test('should enforce unique constraint on User.email', async () => {
      // First user should be created
      await prisma.user.create({
        data: {
          email: 'unique@test.com',
          name: 'Unique User'
        }
      })

      // Second user with same email should fail
      await expect(
        prisma.user.create({
          data: {
            email: 'unique@test.com',
            name: 'Duplicate User'
          }
        })
      ).rejects.toThrow()
    })

    test('should enforce unique constraint on Notebook userId+name', async () => {
      const userId = 'test-user-unique'
      
      // First notebook should be created
      await prisma.notebook.create({
        data: {
          name: 'Unique Notebook',
          order: 0,
          userId
        }
      })

      // Second notebook with same name for same user should fail
      await expect(
        prisma.notebook.create({
          data: {
            name: 'Unique Notebook',
            order: 1,
            userId
          }
        })
      ).rejects.toThrow()
    })
  })

  describe('Default Values', () => {
    test('should have default values for Note table', async () => {
      const note = await prisma.note.create({
        data: {
          content: 'Test content',
          userId: 'test-user-id'
        }
      })

      expect(note.color).toBe('default')
      expect(note.isPinned).toBe(false)
      expect(note.isArchived).toBe(false)
      expect(note.type).toBe('text')
      expect(note.size).toBe('small')
      expect(note.order).toBe(0)
    })

    test('should have default values for UserAISettings', async () => {
      const user = await prisma.user.create({
        data: {
          email: 'default-settings@test.com'
        }
      })

      const settings = await prisma.userAISettings.create({
        data: {
          userId: user.id
        }
      })

      expect(settings.titleSuggestions).toBe(true)
      expect(settings.semanticSearch).toBe(true)
      expect(settings.paragraphRefactor).toBe(true)
      expect(settings.memoryEcho).toBe(true)
      expect(settings.memoryEchoFrequency).toBe('daily')
      expect(settings.aiProvider).toBe('auto')
      expect(settings.preferredLanguage).toBe('auto')
      expect(settings.fontSize).toBe('medium')
      expect(settings.demoMode).toBe(false)
      expect(settings.showRecentNotes).toBe(false)
      expect(settings.emailNotifications).toBe(false)
      expect(settings.desktopNotifications).toBe(false)
      expect(settings.anonymousAnalytics).toBe(false)
    })
  })

  describe('Schema Version Tracking', () => {
    test('should have all migrations applied', async () => {
      // Verify the schema is complete by checking core tables
      const hasUser = await verifyTableExists(prisma, 'User')
      const hasNote = await verifyTableExists(prisma, 'Note')
      const hasAiFeedback = await verifyTableExists(prisma, 'AiFeedback')

      expect(hasUser && hasNote && hasAiFeedback).toBe(true)
    })
  })
})
