#!/usr/bin/env bash
set -euo pipefail
set -E

sqlite_env() {
  : "${SQLITE_DB_NAME:=}"
  : "${SQLITE_DIR:=/var/lib/sqlite}"
  : "${SQLITE_CHANGELOG_FILE:=${CHANGELOG_FILE}}"
  : "${SQLITE_EXT:=.sqlite3}"
  : "${SQLITE_SOURCE:=mysql}"

  # Pragmas
  : "${SQLITE_BUSY_TIMEOUT_MS:=5000}"      # 5s
  : "${SQLITE_CACHE_SIZE_KB:=200000}"      # ~200 MB
  : "${SQLITE_EXTRA_PRAGMAS:=}"            # Custom / Overrides
  : "${SQLITE_MMAP_SIZE_BYTES:=268435456}" # 256 MB
  : "${SQLITE_SYNCHRONOUS:=OFF}"
  : "${SQLITE_WAL_AUTOCHECKPOINT:=100}"    # pages

  # Fallback db names
  if [[ -z "${SQLITE_DB_NAME:-}" ]]; then
    info "[sqlite] SQLITE_DB_NAME not set. Using MYSQL_DB_NAME"
    : "${MYSQL_DB_NAME:?SQLITE_DB_NAME not set and could not be derived}"
    SQLITE_DB_NAME="${MYSQL_DB_NAME}"
  fi
  : "${SQLITE_TEST_DB_NAME:=${SQLITE_DB_NAME}_test}"

  # Sanitize file extension
  [[ "${SQLITE_EXT}" == .* ]] || SQLITE_EXT=".${SQLITE_EXT}"

  # If cloning from mysql
  if [[ "${SQLITE_SOURCE,,}" == "mysql" ]]; then
    # Initialize mysql env variables
    mysql_env
    # Confirm clone tool exists
    _sqlite_get_mysql2sqlite >/dev/null
  fi
}

sqlite_init() {
  try_cmd "sqlite3"
  sqlite_env
}

sqlite_wait_ready() {
  # Nothing to wait for
  return 0
}

sqlite_create_db() {
  _sqlite_create_db "${SQLITE_DB_NAME}"
}

sqlite_create_test_db() {
  _sqlite_create_db "${SQLITE_TEST_DB_NAME}"
}

sqlite_migrate() {
  local db; db="${1:?db name required}"
  local path; path="$(_sqlite_resolve_path "${db}")"

  try_cmd "liquibase"

  info "[sqlite] Migrating database '${path}'"

  liquibase \
    --changeLogFile="${SQLITE_CHANGELOG_FILE}" \
    --contexts="${CONTEXTS}" \
    --driver=org.sqlite.JDBC \
    --url="jdbc:sqlite:${path}" \
    update 2>&1
}

sqlite_mysql_clone() {
  local mysql_db; mysql_db="${1:?mysql db name required}"
  local sqlite_db; sqlite_db="${2:?sqlite db name required}"
  local path; path="$(_sqlite_resolve_path "${sqlite_db}")"

  info "[sqlite] Cloning MySQL '${mysql_db}' -> '${path}'"
  rm -f -- "${path}"

  # Create the db
  "$(_sqlite_get_mysql2sqlite)" \
    --mysql-database "${mysql_db}" \
    --mysql-host "${MYSQL_DB_HOST}" \
    --mysql-password "${MYSQL_DEPLOY_PASSWORD}" \
    --mysql-port "${MYSQL_DB_PORT:-3306}" \
    --mysql-user "${MYSQL_DEPLOY_USER}" \
    --prefix-indices \
    --quiet \
    --sqlite-file "${path}" \
    --vacuum

  _sqlite_apply_pragmas "${path}"
  _sqlite_apply_permissions "${path}"
}

# --- Helpers ---

_sqlite_apply_permissions() {
  local path; path="$1"

  # Ensure permissions for downstream containers
  chgrp sqlite -- "${path}" 2>/dev/null || true
  chmod 2660 -- "${path}" 2>/dev/null || true
  chgrp sqlite -- "${path}-wal" "${path}-shm" 2>/dev/null || true
  chmod 2660 -- "${path}-wal" "${path}-shm" 2>/dev/null || true
}

_sqlite_apply_pragmas() {
  local path; path="$1"

  if [[ "${path}" != ":memory:" ]]; then
    sqlite3 "${path}" > /dev/null <<SQL
PRAGMA cache_size=-${SQLITE_CACHE_SIZE_KB};
PRAGMA journal_mode=WAL;
PRAGMA mmap_size=${SQLITE_MMAP_SIZE_BYTES};
PRAGMA synchronous=${SQLITE_SYNCHRONOUS};
PRAGMA wal_autocheckpoint=${SQLITE_WAL_AUTOCHECKPOINT};
SQL
  fi

  sqlite3 "${path}" > /dev/null <<SQL
PRAGMA auto_vacuum=NONE;
PRAGMA busy_timeout=${SQLITE_BUSY_TIMEOUT_MS};
PRAGMA foreign_keys=ON;
PRAGMA temp_store=MEMORY;
SQL

  # Apply extra PRAGMAs (multi-line supported)
  if [[ -n "${SQLITE_EXTRA_PRAGMAS}" ]]; then
    sqlite3 "${path}" <<< "${SQLITE_EXTRA_PRAGMAS}" >/dev/null || true
  fi
}

_sqlite_create_db() {
  local db; db="${1:?db name required}"
  local path; path="$(_sqlite_resolve_path "${db}")"

  if [[ "${path}" == ":memory:" ]]; then
    info "[sqlite] Using :memory:, skipping file creation"
    return 0
  fi

  info "[sqlite] Creating database '${path}'"

  # Prepare directory
  local dir; dir="$(dirname -- "${path}")"
  mkdir -p -- "${dir}"
  chgrp sqlite -- "${dir}" 2>/dev/null || true
  chmod 2770 -- "${dir}" || true

  # Create DB
  if [[ ! -e "${path}" ]]; then
    sqlite3 "${path}" 'PRAGMA user_version=0;' >/dev/null
  else
    info "[sqlite] Database already exists"
  fi
  _sqlite_apply_pragmas "${path}"
  _sqlite_apply_permissions "${path}"
}

_sqlite_resolve_path() {
  local db_name; db_name="$1"
  if [[ "${db_name}" == ":memory:" || "${db_name}" == */* ]]; then
    printf '%s\n' "${db_name}"
  else
    printf '%s/%s%s\n' "${SQLITE_DIR%/}" "${db_name}" "${SQLITE_EXT}"
  fi
}

_sqlite_get_mysql2sqlite() {
  if has_cmd "mysql-to-sqlite3"; then
    printf "mysql-to-sqlite3"
  elif has_cmd "mysql2sqlite"; then
    printf "mysql2sqlite"
  else
    die "mysql-to-sqlite3 not found. Install techouse/mysql-to-sqlite3"
  fi
}
