import type { SQLiteBunDatabase } from "drizzle-orm/bun-sqlite"
import type { NodeSQLiteDatabase } from "drizzle-orm/node-sqlite"
import { Global } from "@saeeol/core/global"
import path from "path"
import { existsSync } from "fs"
import type { Progress, Options, Stats, Orphans } from "./json-migration-helpers"
import { createStats, listFiles, log } from "./json-migration-helpers"
import {
  migrateProjectsSync,
  migrateSessionsSync,
  migrateMessagesSync,
  migratePartsSync,
  migrateTodosSync,
  migratePermissionsSync,
  migrateSharesSync,
} from "./json-migration-entities"

export type { Progress }

export async function run(db: SQLiteBunDatabase<any, any> | NodeSQLiteDatabase<any, any>, options?: Options) {
  const storageDir = path.join(Global.Path.data, "storage")
  if (!existsSync(storageDir)) {
    log.info("storage directory does not exist, skipping migration")
    return { projects: 0, sessions: 0, messages: 0, parts: 0, todos: 0, permissions: 0, shares: 0, errors: [] as string[] }
  }

  log.info("starting json to sqlite migration", { storageDir })
  const start = performance.now()

  db.run("PRAGMA journal_mode = WAL")
  db.run("PRAGMA synchronous = OFF")
  db.run("PRAGMA cache_size = 10000")
  db.run("PRAGMA temp_store = MEMORY")

  const stats = createStats()
  const orphans: Orphans = { sessions: 0, todos: 0, permissions: 0, shares: 0 }
  const batchSize = 1000
  const now = Date.now()

  log.info("scanning files...")
  const [projectFiles, sessionFiles, messageFiles, partFiles, todoFiles, permFiles, shareFiles] = await Promise.all([
    listFiles("project/*.json", storageDir),
    listFiles("session/*/*.json", storageDir),
    listFiles("message/*/*.json", storageDir),
    listFiles("part/*/*.json", storageDir),
    listFiles("todo/*.json", storageDir),
    listFiles("permission/*.json", storageDir),
    listFiles("session_share/*.json", storageDir),
  ])

  log.info("file scan complete", {
    projects: projectFiles.length, sessions: sessionFiles.length, messages: messageFiles.length,
    parts: partFiles.length, todos: todoFiles.length, permissions: permFiles.length, shares: shareFiles.length,
  })

  const total = Math.max(1, projectFiles.length + sessionFiles.length + messageFiles.length + partFiles.length + todoFiles.length + permFiles.length + shareFiles.length)
  const progress = options?.progress
  let current = 0
  const step = (label: string, count: number) => {
    current = Math.min(total, current + count)
    progress?.({ current, total, label })
  }
  progress?.({ current, total, label: "starting" })

  db.run("BEGIN TRANSACTION")

  const projectIds = await migrateProjectsSync(db, projectFiles, stats, step, batchSize, now)
  const sessionIds = await migrateSessionsSync(db, sessionFiles, stats, orphans, projectIds, step, batchSize, now)
  const messageSessions = await migrateMessagesSync(db, messageFiles, sessionIds, stats, step, batchSize, now)
  await migratePartsSync(db, partFiles, sessionIds, messageSessions, stats, step, batchSize, now)
  await migrateTodosSync(db, todoFiles, sessionIds, stats, orphans, step, batchSize, now)
  await migratePermissionsSync(db, permFiles, projectIds, stats, orphans, step, batchSize)
  await migrateSharesSync(db, shareFiles, sessionIds, stats, orphans, step, batchSize)

  db.run("COMMIT")

  log.info("json migration complete", {
    projects: stats.projects, sessions: stats.sessions, messages: stats.messages, parts: stats.parts,
    todos: stats.todos, permissions: stats.permissions, shares: stats.shares,
    errorCount: stats.errors.length, duration: Math.round(performance.now() - start),
  })
  if (stats.errors.length > 0) log.warn("migration errors", { errors: stats.errors.slice(0, 20) })
  progress?.({ current: total, total, label: "complete" })

  return stats
}

export * as JsonMigration from "./json-migration"
