"""Where the pet was left, so it starts there next time.

Listed in the README as a known limitation: drag it somewhere deliberate, and
the next launch puts it back in the bottom-right corner. For something that
lives on your desktop all day that is a small daily annoyance, and it is a
config write on drag-end.

Lives in the cache dir rather than the repo, next to the restart context: it
is per-machine state about this install, not something that belongs in a git
diff. Every function swallows its own errors — a corrupt or unwritable state
file must never stop the pet from starting, it just means the default corner.

Positions are validated against the *current* screen layout on load, because
the common case for a stale position is exactly the case where it is
dangerous: the pet was last on a monitor that is now unplugged, and restoring
it faithfully would put it somewhere you cannot see or reach.
"""

from __future__ import annotations

import json
import logging
import os
import tempfile
from pathlib import Path
from typing import Optional

logger = logging.getLogger("bolt_pet.window_state")

DEFAULT_PATH = Path.home() / ".cache" / "bolt-pet" / "window.json"


def load(path: Optional[Path] = None) -> Optional[tuple[int, int]]:
    """The saved position, or None if there isn't a usable one."""
    try:
        data = json.loads(Path(path or DEFAULT_PATH).read_text(encoding="utf-8"))
        return int(data["x"]), int(data["y"])
    except (FileNotFoundError, json.JSONDecodeError, KeyError, TypeError, ValueError, OSError):
        return None


def save(x: int, y: int, path: Optional[Path] = None) -> None:
    """Remember where it is now. Atomic, so a crash mid-write can't leave a
    half-file that makes the next start fall back to the corner."""
    target = Path(path or DEFAULT_PATH)
    try:
        target.parent.mkdir(parents=True, exist_ok=True)
        descriptor, temp_path = tempfile.mkstemp(dir=target.parent, prefix=".window_", suffix=".tmp")
        try:
            with os.fdopen(descriptor, "w", encoding="utf-8") as handle:
                json.dump({"x": int(x), "y": int(y)}, handle)
            os.replace(temp_path, target)
        except BaseException:
            try:
                os.unlink(temp_path)
            except OSError:
                pass
            raise
    except Exception:
        logger.debug("Could not save the window position", exc_info=True)


def is_visible_on(x: int, y: int, size: int, rectangles) -> bool:
    """Whether that position still lands on a screen that exists.

    *rectangles* are (left, top, right, bottom) tuples — the caller's job,
    because this module has no business importing Qt. Requires a real overlap
    rather than a touching edge, so a pet saved flush against the boundary of a
    monitor that has since been unplugged is not counted as reachable."""
    for left, top, right, bottom in rectangles:
        overlap_x = min(x + size, right) - max(x, left)
        overlap_y = min(y + size, bottom) - max(y, top)
        if overlap_x > size * 0.25 and overlap_y > size * 0.25:
            return True
    return False
