"""Temp directory management for intermediate encode files.

Owns the shared app cache dir, per-worker temp subdirs (v3-09 race
fix), private-dir mkdir (v3-09 umask defeat), and the per-source-path
hash naming helper. Pure stdlib; no internal package dependencies.

Extracted from ``open-transcode.v3.py`` (QA item v4-03 — package split).
This is a pure code-organization refactor; behavior is identical to v3.
"""

import hashlib
import os
import tempfile
from pathlib import Path

# ──────────────────────────────────────────────
#  TEMP DIRECTORY MANAGEMENT
# ──────────────────────────────────────────────

_APP_CACHE_DIR: Path | None = None

def _get_app_temp_dir() -> Path:
    """Return the shared temp directory for all intermediate files.

    Priority:
      1. ``~/.cache/OpenTranscode/tmp/``  (XDG-compliant, persistent across reboots)
      2. ``/tmp/OpenTranscode/``           (fallback if home cache is unwritable)

    The directory is created on first call.  All temp intermediates
    (pre-scaled MKVs, av1an work dirs) go here so the user's video
    folders stay clean.

    v3 (OTC-013, SEI CERT FIO09-C): the directory is created with
    ``mode=0o700`` so that other users on the system cannot create
    symlinks inside it (which the cleanup sweep would then follow and
    delete arbitrary files). The mode is verified after creation in
    case the directory already existed with looser permissions.
    """
    global _APP_CACHE_DIR
    if _APP_CACHE_DIR is not None:
        return _APP_CACHE_DIR

    # Try XDG cache dir first
    xdg_cache = os.environ.get("XDG_CACHE_HOME", "")
    if xdg_cache:
        candidate = Path(xdg_cache) / "OpenTranscode" / "tmp"
    else:
        candidate = Path.home() / ".cache" / "OpenTranscode" / "tmp"

    if _mkdir_private(candidate):
        _APP_CACHE_DIR = candidate
        return _APP_CACHE_DIR

    # Fallback: /tmp/OpenTranscode
    fallback = Path("/tmp/OpenTranscode")
    if _mkdir_private(fallback):
        _APP_CACHE_DIR = fallback
        return _APP_CACHE_DIR

    # Last resort: system temp
    _APP_CACHE_DIR = Path(tempfile.gettempdir()) / "OpenTranscode"
    _mkdir_private(_APP_CACHE_DIR)
    return _APP_CACHE_DIR


def _mkdir_private(path: Path) -> bool:
    """Create *path* (and parents) with mode 0o700.

    Returns True on success, False on OSError/PermissionError.

    SEI CERT FIO09-C: if the directory already existed with looser
    permissions (e.g. created by a previous version of this app, or by
    another user before us), we attempt to tighten the mode with
    os.chmod(). The chmod may fail silently if we don't own the dir —
    that's an accepted risk, logged but not fatal.
    """
    try:
        path.mkdir(parents=True, exist_ok=True, mode=0o700)
        # mkdir(mode=) is masked by umask; explicitly chmod to be sure
        os.chmod(path, 0o700)
        return True
    except (OSError, PermissionError):
        return False


def _worker_temp_dir(worker_pid: int) -> Path:
    """Return a per-worker temp subdir named by PID.

    v3: each EncoderWorker gets its own subdir under the shared app temp
    dir, so the final cleanup sweep can safely nuke only this worker's
    intermediates without affecting a concurrent worker. The subdir is
    also created with mode=0o700 (FIO09-C).
    """
    base = _get_app_temp_dir()
    sub = base / f"worker-{worker_pid}"
    _mkdir_private(sub)
    return sub


def _temp_path_for(file_path: Path, suffix: str = ".scaled_tmp.mkv",
                   worker_dir: Path | None = None) -> Path:
    """Build a unique temp path for *file_path* inside the app temp dir.

    Uses a short hash of the original absolute path to avoid collisions
    when files in different subdirs share the same stem.

    v3: if *worker_dir* is provided (per-worker subdir), the temp file
    lands there instead of the shared parent. This isolates concurrent
    workers' intermediates from each other.
    """
    tmp_dir = worker_dir if worker_dir is not None else _get_app_temp_dir()
    # Hash the absolute source path for uniqueness
    path_hash = hashlib.sha256(str(file_path.resolve()).encode()).hexdigest()[:12]
    return tmp_dir / f"{file_path.stem}.{path_hash}{suffix}"

