import { createHash } from 'node:crypto'
import fs from 'node:fs/promises'
import path from 'node:path'
import pg from 'pg'

const { Client } = pg

export async function runMigrations(databaseUrl: string, migrationsDir: string, log: (message: string) => void = console.log) {
  const client = new Client({ connectionString: databaseUrl })
  await client.connect()
  try {
    await client.query('CREATE SCHEMA IF NOT EXISTS osint')
    await client.query(`CREATE TABLE IF NOT EXISTS osint.schema_migrations (
      name TEXT PRIMARY KEY,
      checksum TEXT NOT NULL,
      applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
    )`)

    const files = (await fs.readdir(migrationsDir)).filter(name => /^\d+.*\.sql$/.test(name)).sort()
    for (const name of files) {
      const sql = await fs.readFile(path.join(migrationsDir, name), 'utf8')
      const checksum = createHash('sha256').update(sql).digest('hex')
      const existing = await client.query<{ checksum: string }>('SELECT checksum FROM osint.schema_migrations WHERE name = $1', [name])
      if (existing.rows[0]) {
        if (existing.rows[0].checksum !== checksum) throw new Error(`Applied migration was modified: ${name}`)
        log(`skip  ${name}`)
        continue
      }
      await client.query('BEGIN')
      try {
        await client.query(sql)
        await client.query('INSERT INTO osint.schema_migrations (name, checksum) VALUES ($1, $2)', [name, checksum])
        await client.query('COMMIT')
        log(`apply ${name}`)
      } catch (error) {
        await client.query('ROLLBACK')
        throw error
      }
    }
    log('OSINT migrations complete.')
  } finally {
    await client.end()
  }
}
