"""Periodic cleanup of stale /tmp/nexus_upload_* ZIP extract directories.

Removes dirs older than MAX_AGE_HOURS that were left behind after failed/cancelled
pushes or abandoned uploads. Successful manual pushes are already cleaned in sync_engine_v2.
"""

from __future__ import annotations

import asyncio
import logging
import os
import shutil
import time
import glob as glob_module

from server.utils.posix_paths import UPLOAD_STAGING_PREFIX

logger = logging.getLogger("nexus.upload_staging_cleanup")

LOOP_INTERVAL_SECONDS = 3600
MAX_AGE_SECONDS = 24 * 3600


def _cleanup_stale_upload_dirs() -> int:
    """Remove upload staging dirs not modified within MAX_AGE_SECONDS. Returns count removed."""
    now = time.time()
    removed = 0
    pattern = f"{UPLOAD_STAGING_PREFIX}*"
    for path in glob_module.glob(pattern):
        if not os.path.isdir(path):
            continue
        try:
            real = os.path.realpath(path)
            if not real.startswith(UPLOAD_STAGING_PREFIX):
                logger.warning("Skip upload cleanup (path escape): %s", path)
                continue
            age = now - os.path.getmtime(real)
            if age < MAX_AGE_SECONDS:
                continue
            shutil.rmtree(real, ignore_errors=True)
            if not os.path.exists(real):
                removed += 1
                logger.info("Removed stale upload staging dir (age %.0fh): %s", age / 3600, real)
        except OSError as exc:
            logger.warning("Upload staging cleanup skip %s: %s", path, exc)
    return removed


async def upload_staging_cleanup_loop() -> None:
    """Run hourly on primary worker."""
    while True:
        try:
            await asyncio.sleep(LOOP_INTERVAL_SECONDS)
            count = await asyncio.to_thread(_cleanup_stale_upload_dirs)
            if count:
                logger.info("Upload staging cleanup cycle removed %d director(ies)", count)
        except asyncio.CancelledError:
            raise
        except Exception:
            logger.exception("Upload staging cleanup loop error")
