#!/usr/bin/python3
"""Client du daemon labfy-removable-media."""

from __future__ import annotations

import json
import os
import socket
import subprocess
import sys
import time
from pathlib import Path

MAX_RESPONSE = 1024 * 1024
SOCKET_NAME = "labfy-removable-media.sock"
OPERATIONS = {"list", "mount", "unmount", "open", "safe-remove"}


def usage() -> int:
    print(
        "Usage: labfy-removable-mediactl list\n"
        "       labfy-removable-mediactl mount <runtime-id>\n"
        "       labfy-removable-mediactl unmount <runtime-id>\n"
        "       labfy-removable-mediactl open <runtime-id>\n"
        "       labfy-removable-mediactl safe-remove <safe-remove-id>",
        file=sys.stderr,
    )
    return 2


def socket_path() -> Path:
    value = os.environ.get("XDG_RUNTIME_DIR")
    if not value or not os.path.isabs(value):
        raise RuntimeError("XDG_RUNTIME_DIR doit être un chemin absolu")
    return Path(value) / SOCKET_NAME


def activate_service() -> None:
    """Active l'unité sans shell par l'API D-Bus publique de systemd."""
    subprocess.run(
        [
            "/usr/bin/gdbus",
            "call",
            "--session",
            "--dest",
            "org.freedesktop.systemd1",
            "--object-path",
            "/org/freedesktop/systemd1",
            "--method",
            "org.freedesktop.systemd1.Manager.StartUnit",
            "labfy-removable-media.service",
            "replace",
        ],
        stdin=subprocess.DEVNULL,
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
        timeout=5,
        check=False,
    )


def request(path: Path, payload: dict[str, str]) -> dict[str, object]:
    encoded = json.dumps(payload, separators=(",", ":")).encode("utf-8") + b"\n"
    last_error: OSError | None = None
    for attempt in range(21):
        client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        client.settimeout(35)
        try:
            client.connect(str(path))
            client.sendall(encoded)
            data = bytearray()
            while b"\n" not in data:
                chunk = client.recv(4096)
                if not chunk:
                    break
                data.extend(chunk)
                if len(data) > MAX_RESPONSE:
                    raise RuntimeError("Réponse du daemon trop volumineuse")
            if not data or b"\n" not in data:
                raise RuntimeError("Réponse incomplète du daemon")
            value = json.loads(data.partition(b"\n")[0].decode("utf-8"))
            if not isinstance(value, dict):
                raise RuntimeError("Réponse JSON invalide du daemon")
            return value
        except (FileNotFoundError, ConnectionRefusedError) as error:
            last_error = error
            if attempt == 0:
                activate_service()
            time.sleep(0.1)
        finally:
            client.close()
    raise RuntimeError(f"Daemon indisponible: {last_error}")


def main(argv: list[str]) -> int:
    if len(argv) < 2 or argv[1] not in OPERATIONS:
        return usage()
    operation = argv[1]
    expected = 2 if operation == "list" else 3
    if len(argv) != expected:
        return usage()
    payload = {"operation": operation}
    if operation != "list":
        payload["runtime_id"] = argv[2]
    try:
        response = request(socket_path(), payload)
    except Exception as error:
        print(f"labfy-removable-mediactl: {str(error)[:240]}", file=sys.stderr)
        return 1
    if not response.get("ok"):
        print(
            f"labfy-removable-mediactl: {str(response.get('error', 'Erreur inconnue'))[:240]}",
            file=sys.stderr,
        )
        return 1
    state = response.get("state")
    print(json.dumps(state, ensure_ascii=False, separators=(",", ":")))
    return 0


if __name__ == "__main__":
    raise SystemExit(main(sys.argv))
