#!/usr/bin/env bash
# Test: mirror command — storage <-> local database synchronisation
#
# Covered cases:
#   1. Storage-only file   → synced into DB           (UPDATE DATABASE)
#   2. DB-only file        → restored to storage      (UPDATE STORAGE)
#   3. DB-deleted file     → NOT restored to storage  (UPDATE STORAGE skip)
#   4. Both, storage newer → DB updated               (SYNC: STORAGE → DB)
#   5. Both, DB newer      → storage updated          (SYNC: DB → STORAGE)
#
# Not covered (require precise mtime control or artificial conflict injection):
#   - Both, equal mtime → no-op  (EVEN)
#   - Conflicted entry  → skipped
#
set -euo pipefail

SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
CLI_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
cd "$CLI_DIR"
source "$SCRIPT_DIR/test-helpers.sh"
display_test_info

RUN_BUILD="${RUN_BUILD:-1}"
cli_test_init_cli_cmd

WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/livesync-cli-test.XXXXXX")"
trap 'rm -rf "$WORK_DIR"' EXIT

SETTINGS_FILE="$WORK_DIR/data.json"
VAULT_DIR="$WORK_DIR/vault"
DB_DIR="$WORK_DIR/db"
mkdir -p "$VAULT_DIR/test"
mkdir -p "$DB_DIR"

if [[ "$RUN_BUILD" == "1" ]]; then
    echo "[INFO] building CLI..."
    npm run build
fi

echo "[INFO] generating settings -> $SETTINGS_FILE"
cli_test_init_settings_file "$SETTINGS_FILE"

# isConfigured=true is required for mirror (canProceedScan checks this)
cli_test_mark_settings_configured "$SETTINGS_FILE"

# Enable writeDocumentsIfConflicted to resolve unsynced conflicts during mirror
node -e '
const fs = require("fs");
const file = process.argv[1];
const data = JSON.parse(fs.readFileSync(file, "utf-8"));
data.writeDocumentsIfConflicted = true;
fs.writeFileSync(file, JSON.stringify(data, null, 2));
' "$SETTINGS_FILE"

# Preparation: Sync settings and files logic
DB_SETTINGS="$DB_DIR/settings.json"
cp "$SETTINGS_FILE" "$DB_SETTINGS"

# Helper for standard run (Separated paths)
run_mirror_test() {
    run_cli "$DB_DIR" --settings "$DB_SETTINGS" mirror "$VAULT_DIR"
}

# Helper for compatibility run (Same path)
run_mirror_compat() {
    run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" mirror
}

PASS=0
FAIL=0

assert_pass() { echo "[PASS] $1"; PASS=$((PASS + 1)); }
assert_fail() { echo "[FAIL] $1" >&2; FAIL=$((FAIL + 1)); }

# Return timestamp for touch -t in YYYYMMDDHHMM format.
# Accepts offsets such as "+1 hour" or "-1 hour".
portable_touch_timestamp() {
    local offset="$1"
    if command -v gdate >/dev/null 2>&1; then
        gdate -d "$offset" +%Y%m%d%H%M
        return
    fi
    if date -d "$offset" +%Y%m%d%H%M >/dev/null 2>&1; then
        date -d "$offset" +%Y%m%d%H%M
        return
    fi

    case "$offset" in
        "+1 hour")
            date -v+1H +%Y%m%d%H%M
            ;;
        "-1 hour")
            date -v-1H +%Y%m%d%H%M
            ;;
        *)
            echo "[FAIL] Unsupported date offset on this platform: $offset" >&2
            exit 1
            ;;
    esac
}

# ─────────────────────────────────────────────────────────────────────────────
# Case 1: File exists only in storage → should be synced into DB after mirror
# ─────────────────────────────────────────────────────────────────────────────
echo ""
echo "=== Case 1: storage-only → DB (Separated Paths) ==="

printf 'storage-only content\n' > "$VAULT_DIR/test/storage-only.md"

echo "[DEBUG] DB_DIR: $DB_DIR"
echo "[DEBUG] VAULT_DIR: $VAULT_DIR"

run_mirror_test

RESULT_FILE="$WORK_DIR/case1-cat.txt"
# Try 'ls' first to see what's in the DB
echo "--- DB contents ---"
run_cli "$DB_DIR" --settings "$DB_SETTINGS" ls
echo "-------------------"

run_cli "$DB_DIR" --settings "$DB_SETTINGS" pull test/storage-only.md "$RESULT_FILE"

if cmp -s "$VAULT_DIR/test/storage-only.md" "$RESULT_FILE"; then
    assert_pass "storage-only file was synced into DB using separated paths"
else
    assert_fail "storage-only file NOT synced into DB with separated paths"
    echo "--- storage ---" >&2; cat "$VAULT_DIR/test/storage-only.md" >&2
    echo "--- cat ---" >&2;     cat "$RESULT_FILE" >&2
fi

# ─────────────────────────────────────────────────────────────────────────────
# Case 2: File exists only in DB → should be restored to storage after mirror
# ─────────────────────────────────────────────────────────────────────────────
echo ""
echo "=== Case 2: DB-only → storage (Separated Paths) ==="

printf 'db-only content\n' | run_cli "$DB_DIR" --settings "$DB_SETTINGS" put test/db-only.md

if [[ -f "$VAULT_DIR/test/db-only.md" ]]; then
    assert_fail "db-only.md unexpectedly exists in storage before mirror"
else
    echo "[INFO] confirmed: test/db-only.md not in storage before mirror"
fi

run_mirror_test

if [[ -f "$VAULT_DIR/test/db-only.md" ]]; then
    STORAGE_CONTENT="$(cat "$VAULT_DIR/test/db-only.md")"
    if [[ "$STORAGE_CONTENT" == "db-only content" ]]; then
        assert_pass "DB-only file was restored to storage"
    else
        assert_fail "DB-only file restored but content mismatch (got: '${STORAGE_CONTENT}')"
    fi
else
    assert_fail "DB-only file NOT restored to storage after mirror"
fi

# ─────────────────────────────────────────────────────────────────────────────
# Case 3: File deleted in DB → should NOT be created in storage
# ─────────────────────────────────────────────────────────────────────────────
echo ""
echo "=== Case 3: DB-deleted → storage untouched (Separated Paths) ==="

printf 'to-be-deleted\n' | run_cli "$DB_DIR" --settings "$DB_SETTINGS" put test/deleted.md
run_cli "$DB_DIR" --settings "$DB_SETTINGS" rm test/deleted.md

run_mirror_test

if [[ ! -f "$VAULT_DIR/test/deleted.md" ]]; then
    assert_pass "deleted DB entry was not restored to storage"
else
    assert_fail "deleted DB entry was incorrectly restored to storage"
fi

# ─────────────────────────────────────────────────────────────────────────────
# Case 4: Both exist, storage is newer → DB should be updated
# ─────────────────────────────────────────────────────────────────────────────
echo ""
echo "=== Case 4: storage newer → DB updated (Separated Paths) ==="

# Seed DB with old content (mtime ≈ now)
printf 'old content\n' | run_cli "$DB_DIR" --settings "$DB_SETTINGS" put test/sync-storage-newer.md

# Write new content to storage with a timestamp 1 hour in the future
printf 'new content\n' > "$VAULT_DIR/test/sync-storage-newer.md"
touch -t "$(portable_touch_timestamp '+1 hour')" "$VAULT_DIR/test/sync-storage-newer.md"

run_mirror_test

DB_RESULT_FILE="$WORK_DIR/case4-pull.txt"
run_cli "$DB_DIR" --settings "$DB_SETTINGS" pull test/sync-storage-newer.md "$DB_RESULT_FILE"
if cmp -s "$VAULT_DIR/test/sync-storage-newer.md" "$DB_RESULT_FILE"; then
    assert_pass "DB updated to match newer storage file"
else
    assert_fail "DB NOT updated to match newer storage file"
    echo "--- expected(storage) ---" >&2; cat "$VAULT_DIR/test/sync-storage-newer.md" >&2
    echo "--- pulled(from db) ---" >&2;  cat "$DB_RESULT_FILE" >&2
fi

# ─────────────────────────────────────────────────────────────────────────────
# Case 5: Both exist, DB is newer → storage should be updated
# ─────────────────────────────────────────────────────────────────────────────
echo ""
echo "=== Case 5: DB newer → storage updated (Separated Paths) ==="

# Write old content to storage with a timestamp 1 hour in the past
printf 'old storage content\n' > "$VAULT_DIR/test/sync-db-newer.md"
touch -t "$(portable_touch_timestamp '-1 hour')" "$VAULT_DIR/test/sync-db-newer.md"

# Write new content to DB only (mtime ≈ now, newer than the storage file)
printf 'new db content\n' | run_cli "$DB_DIR" --settings "$DB_SETTINGS" put test/sync-db-newer.md

run_mirror_test

STORAGE_CONTENT="$(cat "$VAULT_DIR/test/sync-db-newer.md")"
if [[ "$STORAGE_CONTENT" == "new db content" ]]; then
    assert_pass "storage updated to match newer DB entry"
else
    assert_fail "storage NOT updated to match newer DB entry (got: '${STORAGE_CONTENT}')"
fi

# ─────────────────────────────────────────────────────────────────────────────
# Case 6: Compatibility test - omitted vault-path
# ─────────────────────────────────────────────────────────────────────────────
echo ""
echo "=== Case 6: omitted vault-path (Compatibility Mode) ==="

# We use VAULT_DIR as the "main" database path for this part.
printf 'compat-content\n' > "$VAULT_DIR/compat.md"
run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" mirror

# In compat mode, it should find it in the DB at root
CAT_RESULT="$WORK_DIR/compat-cat.txt"
run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" pull compat.md "$CAT_RESULT"
if [[ "$(cat "$CAT_RESULT")" == "compat-content" ]]; then
    assert_pass "Compatibility mode works (omitted vault-path)"
else
    assert_fail "Compatibility mode failed to sync file into DB"
fi

# ─────────────────────────────────────────────────────────────────────────────
# Summary
# ─────────────────────────────────────────────────────────────────────────────
echo ""
echo "Results: PASS=$PASS FAIL=$FAIL"
if [[ "$FAIL" -gt 0 ]]; then
    exit 1
fi
