"""Stop / Notification / PreToolUse(AskUserQuestion) hook: 作業完了・許可待ち・質問を ntfy でスマホに通知する.

送信先トピックは環境変数 NTFY_TOPIC で指定する（未設定なら何もしない）．トピック名は
知っていれば誰でも購読・送信できるパスワード相当の値のため，リポジトリには書かず
.claude/settings.local.json（gitignore 済み）の env 等で各自設定する．
自前サーバー等へ送る場合は NTFY_SERVER で送信先を変えられる（既定: https://ntfy.sh）．

settings.json では async で配線しているため，本フックの成否は Claude Code の動作に影響しない．
送信に失敗しても黙って終了する．外部サービスを経由するため，本文にコードや会話の内容は含めない．
"""

from __future__ import annotations

import json
import os
import subprocess
import sys
import urllib.request

DEFAULT_SERVER = "https://ntfy.sh"
SEND_TIMEOUT_SEC = 10
GIT_TIMEOUT_SEC = 5
# ntfy の優先度（3 が既定．4 は通知音・バイブが強調される）
PRIORITY_NEEDS_INPUT = 4


def build_notification(data: dict) -> dict | None:
    """イベントから ntfy の通知内容（本文・タグ・優先度）を決める．通知不要なら None を返す."""
    event = data.get("hook_event_name")
    if event == "Stop":
        return {"message": "処理が完了しました", "tags": ["white_check_mark"]}
    if event == "PreToolUse" and data.get("tool_name") == "AskUserQuestion":
        return {"message": "質問への回答を待っています", "tags": ["question"], "priority": PRIORITY_NEEDS_INPUT}
    if event == "Notification":
        if data.get("notification_type") == "permission_prompt":
            return {"message": "ツール実行の許可を待っています", "tags": ["lock"], "priority": PRIORITY_NEEDS_INPUT}
        return {"message": "入力を待っています", "tags": ["speech_balloon"], "priority": PRIORITY_NEEDS_INPUT}
    return None


def project_name(cwd: str) -> str:
    """通知に出すプロジェクト名を返す.

    ホスト OS と dev container でフォルダ名が異なっても同じ名前になるよう，
    origin リモートの URL 末尾（https://github.com/owner/repo.git・git@github.com:owner/repo.git
    のどちらでも repo）を使う．リモートが無い・git が使えない場合はフォルダ名を返す．
    """
    folder = os.path.basename(os.path.normpath(cwd))
    try:
        result = subprocess.run(
            ["git", "-C", cwd, "remote", "get-url", "origin"],
            capture_output=True,
            text=True,
            timeout=GIT_TIMEOUT_SEC,
        )
    except (OSError, subprocess.SubprocessError):
        return folder
    url = result.stdout.strip().rstrip("/")
    if result.returncode != 0 or not url:
        return folder
    name = url.replace(":", "/").rsplit("/", 1)[-1]
    if name.endswith(".git"):  # str.removesuffix は 3.9 以降のため使わない
        name = name[: -len(".git")]
    return name or folder


def main() -> None:
    topic = os.environ.get("NTFY_TOPIC")
    if not topic:
        return

    try:
        data = json.loads(sys.stdin.buffer.read().decode("utf-8"))
    except (ValueError, UnicodeDecodeError):
        return

    notification = build_notification(data)
    if notification is None:
        return

    # 複数プロジェクトを並行して動かしても区別できるよう，タイトルにプロジェクト名を入れる
    project = project_name(data.get("cwd") or os.getcwd())
    payload = {"topic": topic, "title": f"Claude Code ({project})", **notification}

    # JSON で送る（ヘッダー送信だと日本語のタイトルが化けるため）
    server = os.environ.get("NTFY_SERVER", DEFAULT_SERVER).rstrip("/")
    request = urllib.request.Request(
        server,
        data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    try:
        with urllib.request.urlopen(request, timeout=SEND_TIMEOUT_SEC):
            pass
    except OSError:  # URLError・HTTPError・タイムアウトはいずれも OSError の派生
        pass


if __name__ == "__main__":
    main()
