import { execSync } from 'node:child_process'
import { cpSync, mkdirSync, rmSync } from 'node:fs'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { defineConfig } from 'cypress'
import createBundler from '@bahmutov/cypress-esbuild-preprocessor'
import { Client } from 'pg'

const configDirectory = dirname(fileURLToPath(import.meta.url))
const backendDirectory = resolve(configDirectory, '../../backend')
const seedAssetDirectory = resolve(backendDirectory, 'database/seeders/assets')
const socketDirectory = resolve(configDirectory, '../../.postgres')
const storagePublicDirectory = resolve(backendDirectory, 'storage/app/public')
const appDatabase = 'postgres'
const templateDatabase = 'postgres_cypress_template'

function controlClient(): Client {
  return new Client({
    host: socketDirectory,
    database: 'template1',
    user: 'postgres',
  })
}

async function buildTemplate(): Promise<void> {
  const setupClient = controlClient()
  await setupClient.connect()
  await setupClient.query(
    `UPDATE pg_database SET datistemplate = false
       WHERE datname = $1`,
    [templateDatabase],
  )
  await setupClient.query(
    `SELECT pg_terminate_backend(pid) FROM pg_stat_activity
       WHERE datname = $1 AND pid <> pg_backend_pid()`,
    [templateDatabase],
  )
  await setupClient.query(`DROP DATABASE IF EXISTS ${templateDatabase}`)
  await setupClient.query(`CREATE DATABASE ${templateDatabase}`)
  await setupClient.end()

  const seedEnvironment = { ...process.env, DB_DATABASE: templateDatabase }
  execSync('php artisan migrate:fresh --force', {
    cwd: backendDirectory,
    stdio: 'pipe',
    env: seedEnvironment,
  })
  execSync('php artisan db:seed --force', {
    cwd: backendDirectory,
    stdio: 'pipe',
    env: seedEnvironment,
  })
  resetSeedStorage()

  const markClient = controlClient()
  await markClient.connect()
  await markClient.query(
    `UPDATE pg_database SET datistemplate = true
       WHERE datname = $1`,
    [templateDatabase],
  )
  await markClient.end()
}

async function resetFromTemplate(): Promise<null> {
  const client = controlClient()
  await client.connect()
  await client.query(
    `SELECT pg_terminate_backend(pid) FROM pg_stat_activity
       WHERE datname IN ($1, $2) AND pid <> pg_backend_pid()`,
    [appDatabase, templateDatabase],
  )
  await client.query(`DROP DATABASE IF EXISTS ${appDatabase}`)
  await client.query(
    `CREATE DATABASE ${appDatabase}
       TEMPLATE ${templateDatabase}`,
  )
  await client.end()
  resetSeedStorage()

  return null
}

function resetSeedStorage(): void {
  rmSync(resolve(storagePublicDirectory, 'element-icons'), {
    recursive: true,
    force: true,
  })
  rmSync(resolve(storagePublicDirectory, 'element-pdfs'), {
    recursive: true,
    force: true,
  })
  mkdirSync(storagePublicDirectory, { recursive: true })
  cpSync(seedAssetDirectory, storagePublicDirectory, { recursive: true })
}

export default defineConfig({
  e2e: {
    baseUrl: 'http://localhost:5173',
    specPattern: 'cypress/e2e/**/*.cy.ts',
    supportFile: 'cypress/support/e2e.ts',
    fixturesFolder: false,
    video: false,
    screenshotOnRunFailure: false,
    setupNodeEvents(onEvent) {
      onEvent('before:run', async function () {
        await buildTemplate()
      })
      onEvent('file:preprocessor', createBundler())
      onEvent('task', {
        'db:reset': resetFromTemplate,
      })
    },
  },
})
