"""
File-based Temporary Storage

Stores temp files on disk instead of in-memory dict.
This allows multiple Gunicorn workers to share temp files
and survives service restarts within the expiry window.

Files are stored in a temp directory with:
- {file_id}.data - The actual file data
- {file_id}.json - Metadata (filename, timestamp, mime_type, etc.)

IMPORTANT: This module ONLY manages files in the temp directory.
It does NOT touch instance/ (auth database) or any other directories.

All temp files are written to ~/.soosef/temp/ (soosef.paths.TEMP_DIR) so
that the killswitch's destroy_temp_files step covers them.
"""

import json
import os
import platform
import subprocess
import time
from pathlib import Path
from threading import Lock

import soosef.paths as paths

# Default temp directory — always under ~/.soosef/temp/ so the killswitch
# (which purges paths.TEMP_DIR) can reach every file written here.
DEFAULT_TEMP_DIR: Path = paths.TEMP_DIR

# Lock for thread-safe operations
_lock = Lock()

# Module-level temp directory (set on init)
_temp_dir: Path = DEFAULT_TEMP_DIR


def init(temp_dir: Path | str | None = None) -> None:
    """Initialize temp storage with optional custom directory."""
    global _temp_dir
    _temp_dir = Path(temp_dir) if temp_dir else DEFAULT_TEMP_DIR
    _temp_dir.mkdir(parents=True, exist_ok=True)


def _data_path(file_id: str) -> Path:
    """Get path for file data."""
    return _temp_dir / f"{file_id}.data"


def _meta_path(file_id: str) -> Path:
    """Get path for file metadata."""
    return _temp_dir / f"{file_id}.json"


def _thumb_path(thumb_id: str) -> Path:
    """Get path for thumbnail data."""
    return _temp_dir / f"{thumb_id}.thumb"


def _secure_delete(path: Path) -> None:
    """Overwrite and delete a file. Best-effort on flash storage."""
    if not path.exists():
        return

    if platform.system() == "Linux":
        try:
            subprocess.run(
                ["shred", "-u", "-z", "-n", "3", str(path)],
                timeout=30,
                capture_output=True,
            )
            return
        except (subprocess.TimeoutExpired, FileNotFoundError):
            pass

    # Fallback: overwrite with zeros then delete
    try:
        size = path.stat().st_size
        with open(path, "r+b") as f:
            f.write(b"\x00" * size)
            f.flush()
            os.fsync(f.fileno())
        path.unlink()
    except OSError:
        # Last resort: plain unlink so we don't leave data stranded
        path.unlink(missing_ok=True)


def save_temp_file(file_id: str, data: bytes, metadata: dict) -> None:
    """
    Save a temp file with its metadata.

    Args:
        file_id: Unique identifier for the file
        data: File contents as bytes
        metadata: Dict with filename, mime_type, timestamp, etc.
    """
    init()  # Ensure directory exists

    with _lock:
        # Add timestamp if not present
        if "timestamp" not in metadata:
            metadata["timestamp"] = time.time()

        # Write data file
        _data_path(file_id).write_bytes(data)

        # Write metadata
        _meta_path(file_id).write_text(json.dumps(metadata))


def get_temp_file(file_id: str) -> dict | None:
    """
    Get a temp file and its metadata.

    Returns:
        Dict with 'data' (bytes) and all metadata fields, or None if not found.
    """
    init()

    data_file = _data_path(file_id)
    meta_file = _meta_path(file_id)

    if not data_file.exists() or not meta_file.exists():
        return None

    try:
        data = data_file.read_bytes()
        metadata = json.loads(meta_file.read_text())
        return {"data": data, **metadata}
    except (OSError, json.JSONDecodeError):
        return None


def has_temp_file(file_id: str) -> bool:
    """Check if a temp file exists."""
    init()
    return _data_path(file_id).exists() and _meta_path(file_id).exists()


def delete_temp_file(file_id: str) -> None:
    """Securely delete a temp file and its metadata."""
    init()

    with _lock:
        _secure_delete(_data_path(file_id))
        _secure_delete(_meta_path(file_id))


def save_thumbnail(thumb_id: str, data: bytes) -> None:
    """Save a thumbnail."""
    init()

    with _lock:
        _thumb_path(thumb_id).write_bytes(data)


def get_thumbnail(thumb_id: str) -> bytes | None:
    """Get thumbnail data."""
    init()

    thumb_file = _thumb_path(thumb_id)
    if not thumb_file.exists():
        return None

    try:
        return thumb_file.read_bytes()
    except OSError:
        return None


def delete_thumbnail(thumb_id: str) -> None:
    """Securely delete a thumbnail."""
    init()

    with _lock:
        _secure_delete(_thumb_path(thumb_id))


def cleanup_expired(max_age_seconds: float) -> int:
    """
    Securely delete expired temp files.

    Args:
        max_age_seconds: Maximum age in seconds before expiry

    Returns:
        Number of files deleted
    """
    init()

    now = time.time()
    deleted = 0

    with _lock:
        # Find all metadata files
        for meta_file in _temp_dir.glob("*.json"):
            try:
                metadata = json.loads(meta_file.read_text())
                timestamp = metadata.get("timestamp", 0)

                if now - timestamp > max_age_seconds:
                    file_id = meta_file.stem
                    _secure_delete(_data_path(file_id))
                    _secure_delete(meta_file)
                    # Also delete thumbnail if exists
                    _secure_delete(_thumb_path(f"{file_id}_thumb"))
                    deleted += 1
            except (OSError, json.JSONDecodeError):
                # Remove corrupted files
                _secure_delete(meta_file)
                deleted += 1

    return deleted


def cleanup_all() -> int:
    """
    Securely delete all temp files. Call on service start/stop.

    Returns:
        Number of files deleted
    """
    init()

    deleted = 0

    with _lock:
        for f in _temp_dir.iterdir():
            if f.is_file():
                _secure_delete(f)
                deleted += 1

    return deleted


def get_stats() -> dict:
    """Get temp storage statistics."""
    init()

    files = list(_temp_dir.glob("*.data"))
    total_size = sum(f.stat().st_size for f in files if f.exists())

    return {
        "file_count": len(files),
        "total_size_bytes": total_size,
        "temp_dir": str(_temp_dir),
    }
