/**
 * One-shot migration script: SQLite → PostgreSQL
 *
 * Reads data from the SQLite backup (prisma/dev.db) via better-sqlite3,
 * connects to PostgreSQL via Prisma, and inserts all rows while converting
 * JSON string fields to native objects (for Prisma Json type).
 *
 * Usage:
 *   DATABASE_URL="postgresql://keepnotes:keepnotes@localhost:5432/keepnotes" \
 *   npx tsx scripts/migrate-sqlite-to-postgres.ts
 *
 * Prerequisites:
 *   - PostgreSQL running and accessible via DATABASE_URL
 *   - prisma migrate deploy already run (schema exists)
 *   - better-sqlite3 still installed (temporary)
 */

import Database from 'better-sqlite3'
import { PrismaClient } from '../prisma/client-generated'
import * as path from 'path'

const SQLITE_PATH = path.join(__dirname, '..', 'prisma', 'dev.db')

// Parse a JSON string field, returning null if empty/invalid
function parseJsonField(raw: any): any {
  if (raw === null || raw === undefined) return null
  if (typeof raw !== 'string') return raw
  if (raw === '' || raw === 'null') return null
  try {
    return JSON.parse(raw)
  } catch {
    return null
  }
}

// Parse labels specifically — always return array or null
function parseLabels(raw: any): string[] | null {
  const parsed = parseJsonField(raw)
  if (Array.isArray(parsed)) return parsed
  return null
}

// Parse embedding — always return number[] or null
function parseEmbedding(raw: any): number[] | null {
  const parsed = parseJsonField(raw)
  if (Array.isArray(parsed)) return parsed
  return null
}

async function main() {
  console.log('╔══════════════════════════════════════════════════════════╗')
  console.log('║  SQLite → PostgreSQL Migration                          ║')
  console.log('╚══════════════════════════════════════════════════════════╝')
  console.log()

  // 1. Open SQLite
  let sqlite: Database.Database
  try {
    sqlite = new Database(SQLITE_PATH, { readonly: true })
    console.log(`✓ SQLite opened: ${SQLITE_PATH}`)
  } catch (e) {
    console.error(`✗ Cannot open SQLite at ${SQLITE_PATH}: ${e}`)
    process.exit(1)
  }

  // 2. Connect to PostgreSQL via Prisma
  const prisma = new PrismaClient()
  console.log(`✓ PostgreSQL connected via Prisma`)
  console.log()

  // Helper to read all rows from SQLite
  function allRows(sql: string): any[] {
    return sqlite.prepare(sql).all() as any[]
  }

  let totalInserted = 0

  // ── User ──────────────────────────────────────────────────
  console.log('Migrating User...')
  const users = allRows('SELECT * FROM User')
  for (const u of users) {
    await prisma.user.upsert({
      where: { id: u.id },
      update: {},
      create: {
        id: u.id,
        name: u.name,
        email: u.email,
        emailVerified: u.emailVerified ? new Date(u.emailVerified) : null,
        password: u.password,
        role: u.role || 'USER',
        image: u.image,
        theme: u.theme || 'light',
        resetToken: u.resetToken,
        resetTokenExpiry: u.resetTokenExpiry ? new Date(u.resetTokenExpiry) : null,
        createdAt: u.createdAt ? new Date(u.createdAt) : new Date(),
        updatedAt: u.updatedAt ? new Date(u.updatedAt) : new Date(),
      }
    })
  }
  console.log(`  → ${users.length} users`)
  totalInserted += users.length

  // ── Account ───────────────────────────────────────────────
  console.log('Migrating Account...')
  const accounts = allRows('SELECT * FROM Account')
  for (const a of accounts) {
    await prisma.account.create({
      data: {
        userId: a.userId,
        type: a.type,
        provider: a.provider,
        providerAccountId: a.providerAccountId,
        refresh_token: a.refresh_token,
        access_token: a.access_token,
        expires_at: a.expires_at,
        token_type: a.token_type,
        scope: a.scope,
        id_token: a.id_token,
        session_state: a.session_state,
        createdAt: a.createdAt ? new Date(a.createdAt) : new Date(),
        updatedAt: a.updatedAt ? new Date(a.updatedAt) : new Date(),
      }
    }).catch(() => {}) // skip duplicates
  }
  console.log(`  → ${accounts.length} accounts`)
  totalInserted += accounts.length

  // ── Session ───────────────────────────────────────────────
  console.log('Migrating Session...')
  const sessions = allRows('SELECT * FROM Session')
  for (const s of sessions) {
    await prisma.session.create({
      data: {
        sessionToken: s.sessionToken,
        userId: s.userId,
        expires: s.expires ? new Date(s.expires) : new Date(),
        createdAt: s.createdAt ? new Date(s.createdAt) : new Date(),
        updatedAt: s.updatedAt ? new Date(s.updatedAt) : new Date(),
      }
    }).catch(() => {})
  }
  console.log(`  → ${sessions.length} sessions`)
  totalInserted += sessions.length

  // ── Notebook ──────────────────────────────────────────────
  console.log('Migrating Notebook...')
  const notebooks = allRows('SELECT * FROM Notebook')
  for (const nb of notebooks) {
    await prisma.notebook.create({
      data: {
        id: nb.id,
        name: nb.name,
        icon: nb.icon,
        color: nb.color,
        order: nb.order ?? 0,
        userId: nb.userId,
        createdAt: nb.createdAt ? new Date(nb.createdAt) : new Date(),
        updatedAt: nb.updatedAt ? new Date(nb.updatedAt) : new Date(),
      }
    }).catch(() => {})
  }
  console.log(`  → ${notebooks.length} notebooks`)
  totalInserted += notebooks.length

  // ── Label ─────────────────────────────────────────────────
  console.log('Migrating Label...')
  const labels = allRows('SELECT * FROM Label')
  for (const l of labels) {
    await prisma.label.create({
      data: {
        id: l.id,
        name: l.name,
        color: l.color || 'gray',
        notebookId: l.notebookId,
        userId: l.userId,
        createdAt: l.createdAt ? new Date(l.createdAt) : new Date(),
        updatedAt: l.updatedAt ? new Date(l.updatedAt) : new Date(),
      }
    }).catch(() => {})
  }
  console.log(`  → ${labels.length} labels`)
  totalInserted += labels.length

  // ── Note ──────────────────────────────────────────────────
  console.log('Migrating Note...')
  const notes = allRows('SELECT * FROM Note')
  let noteCount = 0
  for (const n of notes) {
    await prisma.note.create({
      data: {
        id: n.id,
        title: n.title,
        content: n.content || '',
        color: n.color || 'default',
        isPinned: n.isPinned === 1 || n.isPinned === true,
        isArchived: n.isArchived === 1 || n.isArchived === true,
        type: n.type || 'text',
        dismissedFromRecent: n.dismissedFromRecent === 1 || n.dismissedFromRecent === true,
        checkItems: parseJsonField(n.checkItems),
        labels: parseLabels(n.labels),
        images: parseJsonField(n.images),
        links: parseJsonField(n.links),
        reminder: n.reminder ? new Date(n.reminder) : null,
        isReminderDone: n.isReminderDone === 1 || n.isReminderDone === true,
        reminderRecurrence: n.reminderRecurrence,
        reminderLocation: n.reminderLocation,
        isMarkdown: n.isMarkdown === 1 || n.isMarkdown === true,
        size: n.size || 'small',
        embedding: parseEmbedding(n.embedding),
        sharedWith: parseJsonField(n.sharedWith),
        userId: n.userId,
        order: n.order ?? 0,
        notebookId: n.notebookId,
        createdAt: n.createdAt ? new Date(n.createdAt) : new Date(),
        updatedAt: n.updatedAt ? new Date(n.updatedAt) : new Date(),
        contentUpdatedAt: n.contentUpdatedAt ? new Date(n.contentUpdatedAt) : new Date(),
        autoGenerated: n.autoGenerated === 1 ? true : n.autoGenerated === 0 ? false : null,
        aiProvider: n.aiProvider,
        aiConfidence: n.aiConfidence,
        language: n.language,
        languageConfidence: n.languageConfidence,
        lastAiAnalysis: n.lastAiAnalysis ? new Date(n.lastAiAnalysis) : null,
      }
    }).catch((e) => {
      console.error(`  Failed note ${n.id}: ${e.message}`)
    })
    noteCount++
  }
  console.log(`  → ${noteCount} notes`)
  totalInserted += noteCount

  // ── NoteShare ─────────────────────────────────────────────
  console.log('Migrating NoteShare...')
  const noteShares = allRows('SELECT * FROM NoteShare')
  for (const ns of noteShares) {
    await prisma.noteShare.create({
      data: {
        id: ns.id,
        noteId: ns.noteId,
        userId: ns.userId,
        sharedBy: ns.sharedBy,
        status: ns.status || 'pending',
        permission: ns.permission || 'view',
        notifiedAt: ns.notifiedAt ? new Date(ns.notifiedAt) : null,
        respondedAt: ns.respondedAt ? new Date(ns.respondedAt) : null,
        createdAt: ns.createdAt ? new Date(ns.createdAt) : new Date(),
        updatedAt: ns.updatedAt ? new Date(ns.updatedAt) : new Date(),
      }
    }).catch(() => {})
  }
  console.log(`  → ${noteShares.length} note shares`)
  totalInserted += noteShares.length

  // ── AiFeedback ────────────────────────────────────────────
  console.log('Migrating AiFeedback...')
  const aiFeedbacks = allRows('SELECT * FROM AiFeedback')
  for (const af of aiFeedbacks) {
    await prisma.aiFeedback.create({
      data: {
        id: af.id,
        noteId: af.noteId,
        userId: af.userId,
        feedbackType: af.feedbackType,
        feature: af.feature,
        originalContent: af.originalContent || '',
        correctedContent: af.correctedContent,
        metadata: parseJsonField(af.metadata),
        createdAt: af.createdAt ? new Date(af.createdAt) : new Date(),
      }
    }).catch(() => {})
  }
  console.log(`  → ${aiFeedbacks.length} ai feedbacks`)
  totalInserted += aiFeedbacks.length

  // ── MemoryEchoInsight ─────────────────────────────────────
  console.log('Migrating MemoryEchoInsight...')
  const insights = allRows('SELECT * FROM MemoryEchoInsight')
  for (const mi of insights) {
    await prisma.memoryEchoInsight.create({
      data: {
        id: mi.id,
        userId: mi.userId,
        note1Id: mi.note1Id,
        note2Id: mi.note2Id,
        similarityScore: mi.similarityScore ?? 0,
        insight: mi.insight || '',
        insightDate: mi.insightDate ? new Date(mi.insightDate) : new Date(),
        viewed: mi.viewed === 1 || mi.viewed === true,
        feedback: mi.feedback,
        dismissed: mi.dismissed === 1 || mi.dismissed === true,
      }
    }).catch(() => {})
  }
  console.log(`  → ${insights.length} memory echo insights`)
  totalInserted += insights.length

  // ── UserAISettings ────────────────────────────────────────
  console.log('Migrating UserAISettings...')
  const aiSettings = allRows('SELECT * FROM UserAISettings')
  for (const s of aiSettings) {
    await prisma.userAISettings.create({
      data: {
        userId: s.userId,
        titleSuggestions: s.titleSuggestions === 1 || s.titleSuggestions === true,
        semanticSearch: s.semanticSearch === 1 || s.semanticSearch === true,
        paragraphRefactor: s.paragraphRefactor === 1 || s.paragraphRefactor === true,
        memoryEcho: s.memoryEcho === 1 || s.memoryEcho === true,
        memoryEchoFrequency: s.memoryEchoFrequency || 'daily',
        aiProvider: s.aiProvider || 'auto',
        preferredLanguage: s.preferredLanguage || 'auto',
        fontSize: s.fontSize || 'medium',
        demoMode: s.demoMode === 1 || s.demoMode === true,
        showRecentNotes: s.showRecentNotes === 1 || s.showRecentNotes === true,
        notesViewMode: s.notesViewMode || 'masonry',
        emailNotifications: s.emailNotifications === 1 || s.emailNotifications === true,
        desktopNotifications: s.desktopNotifications === 1 || s.desktopNotifications === true,
        anonymousAnalytics: s.anonymousAnalytics === 1 || s.anonymousAnalytics === true,
      }
    }).catch(() => {})
  }
  console.log(`  → ${aiSettings.length} user AI settings`)
  totalInserted += aiSettings.length

  // ── SystemConfig ──────────────────────────────────────────
  console.log('Migrating SystemConfig...')
  const configs = allRows('SELECT * FROM SystemConfig')
  for (const c of configs) {
    await prisma.systemConfig.create({
      data: {
        key: c.key,
        value: c.value,
      }
    }).catch(() => {})
  }
  console.log(`  → ${configs.length} system configs`)
  totalInserted += configs.length

  // ── _LabelToNote (many-to-many relations) ─────────────────
  console.log('Migrating Label-Note relations...')
  let relationCount = 0
  try {
    const relations = allRows('SELECT * FROM _LabelToNote')
    for (const r of relations) {
      await prisma.note.update({
        where: { id: r.B },
        data: {
          labelRelations: { connect: { id: r.A } }
        }
      }).catch(() => {})
      relationCount++
    }
  } catch {
    // Table may not exist in older SQLite databases
    console.log('  → _LabelToNote table not found, skipping')
  }
  console.log(`  → ${relationCount} label-note relations`)
  totalInserted += relationCount

  // ── VerificationToken ─────────────────────────────────────
  console.log('Migrating VerificationToken...')
  const tokens = allRows('SELECT * FROM VerificationToken')
  for (const t of tokens) {
    await prisma.verificationToken.create({
      data: {
        identifier: t.identifier,
        token: t.token,
        expires: t.expires ? new Date(t.expires) : new Date(),
      }
    }).catch(() => {})
  }
  console.log(`  → ${tokens.length} verification tokens`)
  totalInserted += tokens.length

  // Cleanup
  sqlite.close()
  await prisma.$disconnect()

  console.log()
  console.log('╔══════════════════════════════════════════════════════════╗')
  console.log(`║  Migration complete: ${totalInserted} total rows inserted        ║`)
  console.log('╚══════════════════════════════════════════════════════════╝')
}

main().catch((e) => {
  console.error('Migration failed:', e)
  process.exit(1)
})
