#!/bin/bash
# =============================================
# backup-myapps.sh — Run on MAIN SERVER
# Backs up: Frappe, Nextcloud, Mautic, n8n, Odoo
# Storage tiers:
#   1. Local  → /root/backups/          (CRITICAL — always runs)
#   2. VM     → SSH tunnel → /backups/main-server/
#   3. Cloud  → Cloudflare R2 (S3-compatible, optional)
#
# NOTE: R2 and VM failures do NOT stop the backup.
#       Local backup always completes first.
# =============================================

set -uo pipefail

BACKUP_DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_NAME="myapps-backup-${BACKUP_DATE}"
BACKUP_DIR="/root/backups/${BACKUP_NAME}"
BACKUP_ARCHIVE="/root/backups/${BACKUP_NAME}.tar.gz"

VM_USER="root"
VM_HOST="178.18.243.51"
VM_PORT="22"
VM_KEY="/root/.ssh/id_rsa"
VM_DEST="/backups/cloudproject/"

# ── Load R2 credentials ───────────────────────────────────────────────────────
CREDENTIALS_FILE="/root/.r2-credentials"
if [ -f "$CREDENTIALS_FILE" ]; then
    set +u
    source "$CREDENTIALS_FILE"
    set -u
fi

# Also try config.py as fallback
if [ -z "${AWS_ACCESS_KEY_ID:-}" ]; then
    CONFIG_PY="/root/management-platform/config.py"
    if [ -f "$CONFIG_PY" ]; then
        _KEY=$(grep -oP '(?<=R2_ACCESS_KEY_ID",\s{5}")[^"]+' "$CONFIG_PY" 2>/dev/null || true)
        _SEC=$(grep -oP '(?<=R2_SECRET_ACCESS_KEY", ")[^"]+' "$CONFIG_PY" 2>/dev/null || true)
        _BKT=$(grep -oP '(?<=R2_BUCKET_NAME",\s{7}")[^"]+' "$CONFIG_PY" 2>/dev/null || true)
        [ -n "$_KEY" ] && export AWS_ACCESS_KEY_ID="$_KEY"
        [ -n "$_SEC" ] && export AWS_SECRET_ACCESS_KEY="$_SEC"
        [ -n "$_BKT" ] && export R2_BUCKET_NAME="$_BKT"
    fi
fi

R2_ACCOUNT_ID="${R2_ACCOUNT_ID:-35e00c230cc8066252a2d9890b69aea2}"
R2_BUCKET="${R2_BUCKET_NAME:-navitrends-backups}"
R2_ENDPOINT="https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com"

BACKUP_LOG_FILE="/root/backups/backup-status.log"
MAX_BACKUPS=10
MAX_R2_BACKUPS=5

TIER1_STATUS="pending"
TIER2_STATUS="pending"
TIER3_STATUS="pending"

log_status() {
    local status="$1" name="$2" msg="${3:-}"
    echo "$(date '+%Y-%m-%d %H:%M:%S') | ${status} | ${name} | ${msg}" >> "$BACKUP_LOG_FILE"
}

echo "========================================="
echo "📦 Starting Backup: $BACKUP_NAME"
echo "   Apps: Frappe, Nextcloud, Mautic, n8n, Odoo"
echo "   Tiers: Local → VM → ☁ Cloudflare R2"
echo "========================================="

mkdir -p "$BACKUP_DIR" "/root/backups"
cd "$BACKUP_DIR"

# --------------------------------------------------
# 1. Docker container list
# --------------------------------------------------
echo ""
echo "📋 [1/7] Saving Docker container list..."
docker ps -a \
    --filter "name=frappe" --filter "name=nextcloud" \
    --filter "name=mautic" --filter "name=n8n" --filter "name=odoo" \
    --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}" \
  > docker-containers.txt 2>/dev/null || true
echo "   ✅ Done"

# --------------------------------------------------
# 2. docker-compose files
# --------------------------------------------------
echo ""
echo "📄 [2/7] Saving docker-compose files..."
mkdir -p compose-files
for app in frappe-setup odoo-clean nextcloud-setup mautic-setup n8n-setup; do
    if [ -d ~/$app ]; then
        cp -r ~/$app compose-files/ && echo "   ✅ $app" || echo "   ⚠️  $app copy failed"
    else
        echo "   ⏭️  $app not found — skipping"
    fi
done

# --------------------------------------------------
# 3. Docker volumes
# --------------------------------------------------
echo ""
echo "💾 [3/7] Backing up Docker volumes..."
mkdir -p volumes

VOLUMES=(
    "frappe-setup_frappe-sites"
    "frappe-setup_mariadb-data"
    "nextcloud-setup_nextcloud-data"
    "nextcloud-setup_nextcloud-db-data"
    "mautic-setup_mautic-data"
    "mautic-setup_mautic-db-data"
    "n8n-setup_n8n-data"
    "n8n-setup_n8n-db-data"
    "odoo-clean_db-data"
    "odoo-clean_odoo-etc"
)

for volume in "${VOLUMES[@]}"; do
    if ! docker volume inspect "$volume" &>/dev/null; then
        echo "   ⏭️  $volume — not found, skipping"
        continue
    fi
    echo -n "   📁 $volume ... "
    docker run --rm \
        -v "${volume}:/source:ro" \
        -v "$(pwd)/volumes:/backup" \
        alpine tar czf "/backup/${volume}.tar.gz" -C /source . \
    && echo "✅" || echo "⚠️  FAILED (continuing)"
done

# --------------------------------------------------
# 4. Container inspect configs
# --------------------------------------------------
echo ""
echo "🔧 [4/7] Saving container inspect configs..."
mkdir -p container-configs
COUNT=0
while IFS= read -r cid; do
    name=$(docker inspect --format='{{.Name}}' "$cid" 2>/dev/null | sed 's/\///')
    if [ -n "$name" ]; then
        docker inspect "$cid" > "container-configs/${name}.json" 2>/dev/null \
            && COUNT=$((COUNT+1)) || true
    fi
done < <(docker ps -a \
    --filter "name=frappe" --filter "name=nextcloud" \
    --filter "name=mautic" --filter "name=n8n" --filter "name=odoo" \
    --format "{{.ID}}" 2>/dev/null || true)
echo "   ✅ Saved $COUNT container configs"

# --------------------------------------------------
# 5. App config files
# --------------------------------------------------
echo ""
echo "⚙️  [5/7] Extracting app config files..."
mkdir -p configs

docker run --rm -v nextcloud-setup_nextcloud-data:/source:ro \
    alpine cat /source/config/config.php > configs/nextcloud-config.php 2>/dev/null \
    && echo "   ✅ Nextcloud config.php" \
    || echo "   ⏭️  Nextcloud config not found (skipping)"

docker exec frappe-erpnext \
    cat /home/frappe/frappe-bench/sites/erpnext.navitrends.ovh/site_config.json \
    > configs/frappe-site_config.json 2>/dev/null \
    && echo "   ✅ Frappe site_config.json" \
    || echo "   ⏭️  Frappe config not found (skipping)"

# --------------------------------------------------
# 6. Metadata + checksum
# --------------------------------------------------
echo ""
echo "📝 [6/7] Writing backup metadata..."
VOLUME_COUNT=$(ls volumes/*.tar.gz 2>/dev/null | wc -l)

cat > backup-info.txt << EOF
Backup Name:  $BACKUP_NAME
Backup Date:  $(date)
Hostname:     $(hostname)
Apps:         Frappe, Nextcloud, Mautic, n8n, Odoo
Volumes:      $VOLUME_COUNT volume(s) backed up
Docker info:  $(docker --version 2>/dev/null || echo 'N/A')
Storage Tiers:
  - Local:  /root/backups/
  - VM:     ${VM_HOST}:${VM_PORT} → ${VM_DEST}
  - Cloud:  Cloudflare R2 → s3://${R2_BUCKET}/backups/

Volumes included:
$(ls volumes/*.tar.gz 2>/dev/null | xargs -I{} basename {} 2>/dev/null || echo "none")
EOF

echo "" >> backup-info.txt
echo "Volume SHA256 checksums:" >> backup-info.txt
for f in volumes/*.tar.gz; do
    [ -f "$f" ] && sha256sum "$f" | awk '{print $1 "  " $2}' >> backup-info.txt || true
done
echo "   ✅ Done"

# --------------------------------------------------
# 7. Compress
# --------------------------------------------------
echo ""
echo "🗜️  [7/7] Compressing backup..."
cd /root/backups

tar -czf "${BACKUP_NAME}.tar.gz" "${BACKUP_NAME}/" 2>/dev/null
if [ $? -ne 0 ]; then
    echo "   ❌ CRITICAL: Compression failed!"
    log_status "FAILED" "$BACKUP_NAME" "compression_failed"
    exit 1
fi

COMPRESSED_SIZE=$(du -h "${BACKUP_NAME}.tar.gz" | cut -f1)
echo "   ✅ Compressed size: $COMPRESSED_SIZE → $BACKUP_ARCHIVE"

sha256sum "${BACKUP_NAME}.tar.gz" > "${BACKUP_NAME}.tar.gz.sha256"
echo "   ✅ Checksum written"

rm -rf "$BACKUP_DIR"
TIER1_STATUS="ok:${COMPRESSED_SIZE}"
echo ""
echo "✅ [TIER 1] Local backup complete: $BACKUP_ARCHIVE ($COMPRESSED_SIZE)"

# --------------------------------------------------
# 8. Retention — keep only MAX_BACKUPS locally
# --------------------------------------------------
echo ""
echo "🧹 [Retention] Keeping latest ${MAX_BACKUPS} local backups..."
ARCHIVE_LIST=$(ls -t /root/backups/myapps-backup-*.tar.gz 2>/dev/null || true)
ARCHIVE_COUNT=$(echo "$ARCHIVE_LIST" | grep -c '.tar.gz' 2>/dev/null || echo "0")

if [ "$ARCHIVE_COUNT" -gt "$MAX_BACKUPS" ]; then
    TO_DELETE=$(echo "$ARCHIVE_LIST" | tail -n +$((MAX_BACKUPS + 1)))
    while IFS= read -r old_file; do
        [ -z "$old_file" ] && continue
        rm -f "$old_file" "${old_file}.sha256"
        echo "   🗑️  Deleted: $(basename $old_file)"
    done <<< "$TO_DELETE"
else
    echo "   ✅ ${ARCHIVE_COUNT}/${MAX_BACKUPS} backups — nothing to prune"
fi

# --------------------------------------------------
# 9. VM transfer [TIER 2] — non-fatal
# --------------------------------------------------
echo ""
echo "📤 [TIER 2] Sending backup to VM (${VM_HOST}:${VM_PORT})..."

scp -i "$VM_KEY" -P "$VM_PORT" \
    -o StrictHostKeyChecking=no -o ConnectTimeout=30 \
    "${BACKUP_NAME}.tar.gz" \
    "${VM_USER}@${VM_HOST}:${VM_DEST}" 2>/dev/null
VM_SCP_RC=$?

if [ $VM_SCP_RC -eq 0 ]; then
    echo "   ✅ Backup sent to VM successfully!"
    scp -i "$VM_KEY" -P "$VM_PORT" \
        -o StrictHostKeyChecking=no -o ConnectTimeout=15 \
        "${BACKUP_NAME}.tar.gz.sha256" \
        "${VM_USER}@${VM_HOST}:${VM_DEST}" 2>/dev/null || true
    TIER2_STATUS="ok"
else
    echo "   ⚠️  VM transfer failed (rc=$VM_SCP_RC) — local backup is safe, continuing..."
    TIER2_STATUS="failed:rc=${VM_SCP_RC}"
fi

# --------------------------------------------------
# 10. Cloudflare R2 [TIER 3] — non-fatal
# --------------------------------------------------
echo ""
echo "☁️  [TIER 3] Uploading to Cloudflare R2..."

if [ -z "${AWS_ACCESS_KEY_ID:-}" ] || [ -z "${AWS_SECRET_ACCESS_KEY:-}" ]; then
    echo "   ⚠️  R2 credentials not found — skipping cloud upload"
    echo "   💡 Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in /root/.r2-credentials"
    TIER3_STATUS="skipped:no_credentials"
else
    echo "   Credentials: found (key=${AWS_ACCESS_KEY_ID:0:8}...)"
    echo "   Bucket:      ${R2_BUCKET}"
    echo "   Endpoint:    ${R2_ENDPOINT}"

    PYTHON_BIN="/root/management-platform/venv/bin/python3"
    if [ ! -f "$PYTHON_BIN" ]; then
        PYTHON_BIN="python3"
    fi

    R2_SCRIPT="/tmp/r2_upload_$$.py"
    cat > "$R2_SCRIPT" << PYEOF
import sys, os, boto3
from datetime import datetime, timezone

account_id  = "${R2_ACCOUNT_ID}"
bucket      = "${R2_BUCKET}"
endpoint    = "https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com"
access_key  = "${AWS_ACCESS_KEY_ID}"
secret_key  = "${AWS_SECRET_ACCESS_KEY}"
archive     = "/root/backups/${BACKUP_NAME}.tar.gz"
sha_file    = archive + '.sha256'
key         = "backups/${BACKUP_NAME}.tar.gz"
max_r2      = ${MAX_R2_BACKUPS}

try:
    client = boto3.client(
        's3',
        endpoint_url=endpoint,
        aws_access_key_id=access_key,
        aws_secret_access_key=secret_key,
        region_name='auto'
    )

    # Ensure bucket exists
    try:
        client.head_bucket(Bucket=bucket)
    except Exception:
        client.create_bucket(Bucket=bucket)
        print(f'   Created bucket: {bucket}')

    # Upload archive
    size_mb   = os.path.getsize(archive) / 1024 / 1024
    timestamp = datetime.now(timezone.utc).isoformat()
    print(f'   Uploading {size_mb:.1f} MB to r2://{bucket}/{key}')
    client.upload_file(
        archive, bucket, key,
        ExtraArgs={"Metadata": {
            "uploaded-by":   "backup-script",
            "uploaded-at":   timestamp,
            "original-file": archive.split("/")[-1],
        }}
    )
    print('   ✅ R2 archive uploaded successfully')

    # Upload sha256 companion
    if os.path.exists(sha_file):
        client.upload_file(sha_file, bucket, key + '.sha256')
        print('   ✅ R2 SHA256 checksum uploaded')

    # ── R2 Retention: keep only max_r2 latest backups ──────────
    print(f'   🧹 Enforcing R2 retention (max {max_r2})...')
    resp    = client.list_objects_v2(Bucket=bucket, Prefix='backups/')
    objects = sorted(
        [o for o in resp.get('Contents', []) if not o['Key'].endswith('.sha256')],
        key=lambda x: x['LastModified'],
        reverse=True  # newest first
    )
    to_delete = objects[max_r2:]
    if not to_delete:
        print(f'   ✅ {len(objects)}/{max_r2} R2 backups — nothing to prune')
    for obj in to_delete:
        old_key = obj['Key']
        client.delete_object(Bucket=bucket, Key=old_key)
        try:
            client.delete_object(Bucket=bucket, Key=old_key + '.sha256')
        except Exception:
            pass
        print(f'   🗑️  Deleted from R2: {old_key.replace("backups/", "")}')

    sys.exit(0)

except Exception as e:
    print(f'   ERROR: {e}')
    sys.exit(2)
PYEOF

    "$PYTHON_BIN" "$R2_SCRIPT"
    R2_RC=$?
    rm -f "$R2_SCRIPT"

    if [ $R2_RC -eq 0 ]; then
        echo "   ✅ R2 upload + retention complete"
        TIER3_STATUS="ok"
    else
        echo "   ⚠️  R2 upload failed (rc=$R2_RC) — local backup is safe at: $BACKUP_ARCHIVE"
        TIER3_STATUS="failed:rc=${R2_RC}"
    fi
fi

# --------------------------------------------------
# 11. Final summary + log
# --------------------------------------------------
echo ""
echo "========================================="
echo "✅ BACKUP COMPLETE"
echo ""
echo "   📦 Name:  $BACKUP_NAME"
echo "   💾 Local: $BACKUP_ARCHIVE ($COMPRESSED_SIZE)"
echo "   🖥️  VM:    ${TIER2_STATUS}"
echo "   ☁️  R2:    ${TIER3_STATUS}"
echo "========================================="

log_status "SUCCESS" "$BACKUP_NAME" \
    "size=${COMPRESSED_SIZE} | tier1=${TIER1_STATUS} | tier2=${TIER2_STATUS} | tier3=${TIER3_STATUS}"

# ── Notification on failure (optional) ───────────────────────────────────────
notify_failure() {
    echo "Backup FAILED: $BACKUP_NAME" | \
    mail -s "[Navitrends] BACKUP FAILED - $(date)" ameniboukottaya@gmail.com
}