#!/usr/bin/env bash

set -euo pipefail

script_path=${BASH_SOURCE[0]}
if [[ "$script_path" == */* ]]; then
  script_dir=${script_path%/*}
else
  script_dir=.
fi

cd "$script_dir/.."
root_dir=$PWD

if [[ -n "${CONTAINER_ENGINE:-}" ]]; then
  engine=$CONTAINER_ENGINE
elif command -v podman >/dev/null 2>&1; then
  engine=podman
elif command -v docker >/dev/null 2>&1; then
  engine=docker
else
  printf 'Need podman or docker in PATH.\n' >&2
  exit 1
fi

if ! command -v "$engine" >/dev/null 2>&1; then
  printf 'Container engine not found: %s\n' "$engine" >&2
  exit 1
fi

image=${FILES_DEV_IMAGE:-files-dev-nextjs:latest}
container=${FILES_DEV_CONTAINER:-files-dev-nextjs}
host_port=${FILES_DEV_PORT:-3000}
dev_dir=${FILES_DEV_DIR:-$root_dir/.dev}
app_data_dir=$dev_dir/app-data
storage_dir=$dev_dir/storage
password_hash='$2a$12$8d4a4zH/tb5TYiP4wQpuFO4GheZarYDWVDGl2S90R4xdMd83JOPMm'

volume_arg() {
  if [[ "$engine" == podman ]]; then
    printf '%s:%s:Z' "$1" "$2"
  else
    printf '%s:%s' "$1" "$2"
  fi
}

mkdir -p "$app_data_dir" "$storage_dir/_share"

printf 'Using %s\n' "$engine"
printf 'Building %s\n' "$image"
"$engine" build -t "$image" "$root_dir/nextjs"

printf 'Seeding dev user john.doe / 123456\n'
"$engine" run --rm -i \
  -e DB_PATH=/app/data/uploads.sqlite \
  -e DEV_PASSWORD_HASH="$password_hash" \
  -v "$(volume_arg "$app_data_dir" /app/data)" \
  -v "$(volume_arg "$storage_dir" /data)" \
  "$image" node - <<'NODE'
const fs = require('node:fs');
const path = require('node:path');
const sqlite3 = require('sqlite3');

const dbPath = process.env.DB_PATH || '/app/data/uploads.sqlite';
const passwordHash = process.env.DEV_PASSWORD_HASH;

fs.mkdirSync(path.dirname(dbPath), { recursive: true });
fs.mkdirSync('/data/_share', { recursive: true });

const db = new sqlite3.Database(dbPath);

function run(sql, params = []) {
  return new Promise((resolve, reject) => {
    db.run(sql, params, function onRun(error) {
      if (error) {
        reject(error);
        return;
      }
      resolve(this);
    });
  });
}

function close() {
  return new Promise((resolve, reject) => {
    db.close((error) => {
      if (error) {
        reject(error);
        return;
      }
      resolve();
    });
  });
}

(async () => {
  await run(`CREATE TABLE IF NOT EXISTS users (
    username TEXT PRIMARY KEY,
    password_hash TEXT NOT NULL,
    created_at INTEGER NOT NULL,
    is_admin INTEGER NOT NULL DEFAULT 0
  )`);
  await run('ALTER TABLE users ADD COLUMN is_admin INTEGER NOT NULL DEFAULT 0').catch(() => undefined);
  await run(
    `INSERT INTO users (username, password_hash, created_at, is_admin)
     VALUES (?, ?, ?, 1)
     ON CONFLICT(username) DO UPDATE SET password_hash = excluded.password_hash, is_admin = 1`,
    ['john.doe', passwordHash, Date.now()]
  );
  await close();
})().catch(async (error) => {
  try {
    await close();
  } catch {
  }
  console.error(error);
  process.exit(1);
});
NODE

if "$engine" container inspect "$container" >/dev/null 2>&1; then
  printf 'Removing old %s\n' "$container"
  "$engine" rm -f "$container" >/dev/null
fi

logs_pid=
watchdog_pid=

stop_watchdog() {
  if [[ -n "$watchdog_pid" ]]; then
    kill "$watchdog_pid" >/dev/null 2>&1 || true
    wait "$watchdog_pid" >/dev/null 2>&1 || true
    watchdog_pid=
  fi
}

cleanup() {
  status=$?
  trap - EXIT INT TERM HUP QUIT

  stop_watchdog

  if [[ -n "$logs_pid" ]]; then
    kill "$logs_pid" >/dev/null 2>&1 || true
    wait "$logs_pid" >/dev/null 2>&1 || true
  fi

  if "$engine" container inspect "$container" >/dev/null 2>&1; then
    printf '\nKilling %s\n' "$container" >&2
    "$engine" rm -f "$container" >/dev/null 2>&1 || true
  fi

  exit "$status"
}

start_watchdog() {
  local parent_pid=$$
  (
    trap '' INT TERM HUP QUIT
    while kill -0 "$parent_pid" >/dev/null 2>&1; do
      sleep 0.2
    done
    "$engine" rm -f "$container" >/dev/null 2>&1 || true
  ) &
  watchdog_pid=$!
}

trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
trap 'exit 129' HUP
trap 'exit 131' QUIT

printf '\nPreview: http://localhost:%s\n' "$host_port"
printf 'User login:  john.doe / 123456\n'
printf 'Admin area:  /manage/admin, password 123456\n'
printf 'Data dir:    %s\n' "$dev_dir"
printf 'Stop:        Ctrl-C\n\n'

"$engine" run --rm -d \
  --name "$container" \
  --stop-timeout 1 \
  -p "127.0.0.1:$host_port:3000" \
  -e DATA_DIR=/data \
  -e DB_PATH=/app/data/uploads.sqlite \
  -e SERVICE_FQDN=localhost \
  -e PUBLIC_BASE_URL="http://localhost:$host_port" \
  -e UPLOAD_TTL_SECONDS=604800 \
  -e UPLOAD_MAX_BYTES=26843545600 \
  -e MANAGEMENT_ADMIN_HASH="$password_hash" \
  -e COOKIE_SECURE=false \
  -e SMTP_HOST= \
  -e SMTP_PORT=587 \
  -e SMTP_USER= \
  -e SMTP_PASS= \
  -e SMTP_MAIL= \
  -e SMTP_NAME= \
  -e PORT=3000 \
  -v "$(volume_arg "$app_data_dir" /app/data)" \
  -v "$(volume_arg "$storage_dir" /data)" \
  "$image" >/dev/null

start_watchdog

"$engine" logs -f "$container" &
logs_pid=$!

set +e
container_status=$("$engine" wait "$container")
wait_status=$?
set -e

kill "$logs_pid" >/dev/null 2>&1 || true
wait "$logs_pid" >/dev/null 2>&1 || true
logs_pid=
stop_watchdog

trap - EXIT INT TERM HUP QUIT

if [[ $wait_status -ne 0 ]]; then
  exit "$wait_status"
fi

if [[ "$container_status" =~ ^[0-9]+$ ]]; then
  exit "$container_status"
fi
