"""Platform-correct filesystem locations for atcwatch."""

from __future__ import annotations

import os
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Mapping


@dataclass(frozen=True)
class AtcwatchPaths:
    config: Path
    watchlist: Path
    state: Path
    lock: Path
    log: Path


# Public name used by the orchestration layer.
Paths = AtcwatchPaths

# What an unstamped checkout reports.  Not a placeholder to be replaced later:
# a tree running straight from git genuinely has no release number, and saying
# "dev" is the honest answer.
DEV_VERSION = "dev"


def package_version() -> str:
    """The release this package was cut from, or ``dev``.

    ``version.txt`` is written into the tarball at deploy time from the git
    tag.  It is deliberately absent from the repository: the number lives in
    the tag, and a copy in git would silently rot whenever a release forgets
    to update it.
    """

    try:
        raw = Path(__file__).with_name("version.txt").read_text(encoding="utf-8")
    except OSError:
        return DEV_VERSION
    # First line only, and bounded: this string reaches a menu entry and the
    # --version output, so a damaged file must not wrap the tray menu.
    first = raw.strip().splitlines()[0].strip() if raw.strip() else ""
    return first[:32] or DEV_VERSION


def _absolute(value: str, name: str) -> Path:
    path = Path(value).expanduser()
    if not path.is_absolute():
        raise ValueError(f"{name} must be an absolute path")
    return path


def resolve_paths(
    *, env: Mapping[str, str] | None = None, platform: str | None = None,
    home: Path | None = None,
) -> AtcwatchPaths:
    values = os.environ if env is None else env
    system = sys.platform if platform is None else platform
    override = values.get("ATCWATCH_HOME")
    if override:
        config_dir = state_dir = _absolute(override, "ATCWATCH_HOME")
    else:
        user_home = Path.home() if home is None else Path(home)
        # The browser-owned config/watchlist pair intentionally stays in one
        # visible directory; mutable state uses the platform state location.
        config_dir = user_home / "atcwatch"
        if system.startswith("linux"):
            state_base = (_absolute(values["XDG_STATE_HOME"], "XDG_STATE_HOME")
                          if values.get("XDG_STATE_HOME")
                          else user_home / ".local" / "state")
            state_dir = state_base / "atcwatch"
        elif system == "darwin":
            state_dir = user_home / "Library" / "Application Support" / "atcwatch"
        else:
            raise RuntimeError(f"unsupported platform for atcwatch paths: {system}")
    return AtcwatchPaths(
        config=config_dir / "config.json",
        watchlist=config_dir / "watchlist.json",
        state=state_dir / "state.json",
        lock=state_dir / "atcwatch.lock",
        log=state_dir / "atcwatch.log",
    )
