"""
Slack App Setup Script - Health Bot
====================================
自动创建 Butler Health Bot 所需的 Slack App 及权限配置。

两种使用方式:
  1. 有 Configuration Token (推荐):
     python scripts/setup_slack_app.py --config-token xoxe-...
     → 直接调 API 创建 app, 输出后续步骤

  2. 无 Token (只生成 Manifest):
     python scripts/setup_slack_app.py --print-manifest
     → 打印 JSON, 粘贴到 api.slack.com/apps → Create App → From manifest

如何获取 Configuration Token:
  1. 打开 https://api.slack.com/apps
  2. 点右上角用户头像 → "Your apps"
  3. 点 "Generate Token" (或者 https://api.slack.com/apps?new_granular_bot_app=1)
  4. 选择 workspace, 勾选 "authorizations:read" + "authorizations:write"
  5. 复制 Token (xoxe-... 格式)
"""

import json
import sys
import argparse
import urllib.request
import urllib.error
from pathlib import Path

# ─── App Manifest Definition ────────────────────────────────────────────────
# 这里完整描述了 Health Bot 所需的全部权限和配置

HEALTH_BOT_MANIFEST: dict = {
    "display_information": {
        "name": "Butler Health Bot",
        "description": "Personal health assistant - Garmin data, sleep, HRV, activity tracking",
        "background_color": "#1B5E20",
    },
    "features": {
        "bot_user": {
            "display_name": "butler",
            "always_online": True,
        }
    },
    "oauth_config": {
        "scopes": {
            "bot": [
                # 发送和更新消息
                "chat:write",        # 发消息到已加入的频道
                "chat:write.public", # 发消息到未加入的公开频道
                # 读取消息事件 (Socket Mode 下接收用户消息)
                "channels:history",  # 公开频道的消息历史
                "groups:history",    # 私有频道的消息历史
                "im:history",        # DM 消息历史
                "mpim:history",      # 群组 DM 消息历史
                # 文件处理 (接收图片/文件, 上传报告)
                "files:read",        # 读取用户上传的文件
                "files:write",       # 上传文件 (周报等)
            ]
        }
    },
    "settings": {
        "event_subscriptions": {
            "bot_events": [
                "message.channels",  # 公开频道消息
                "message.groups",    # 私有频道消息
                "message.im",        # DM 消息
                "message.mpim",      # 群组 DM 消息
            ]
        },
        "interactivity": {
            "is_enabled": False      # 不使用 Slash Commands / Modals
        },
        "org_deploy_enabled": False,
        "socket_mode_enabled": True,  # 使用 Socket Mode (无需公网 URL)
        "token_rotation_enabled": False,
    },
}

# ─── Slack API Helpers ───────────────────────────────────────────────────────

SLACK_API_BASE = "https://slack.com/api"


def _slack_api_call(method: str, config_token: str, payload: dict) -> dict:
    """Call a Slack API method with a Configuration Token."""
    url = f"{SLACK_API_BASE}/{method}"
    data = json.dumps(payload).encode("utf-8")
    req = urllib.request.Request(
        url,
        data=data,
        headers={
            "Authorization": f"Bearer {config_token}",
            "Content-Type": "application/json; charset=utf-8",
        },
    )
    try:
        with urllib.request.urlopen(req, timeout=15) as resp:
            result = json.loads(resp.read().decode("utf-8"))
    except urllib.error.HTTPError as e:
        body = e.read().decode("utf-8", errors="replace")
        print(f"[ERROR] HTTP {e.code}: {body}", file=sys.stderr)
        sys.exit(1)
    except urllib.error.URLError as e:
        print(f"[ERROR] Network error: {e.reason}", file=sys.stderr)
        sys.exit(1)
    return result


def create_app(config_token: str, manifest: dict) -> dict:
    """Create a Slack app from a manifest using apps.manifest.create."""
    print("→ Calling apps.manifest.create ...")
    result = _slack_api_call(
        "apps.manifest.create",
        config_token,
        {"manifest": manifest},
    )
    return result


def generate_app_token(config_token: str, app_id: str) -> str | None:
    """
    Generate an App-Level Token (xapp-) with connections:write scope.
    Required for Socket Mode.
    """
    print("→ Generating App-Level Token (Socket Mode) ...")
    result = _slack_api_call(
        "apps.connections.open",  # NOTE: 这个 API 不存在,仅示意
        config_token,
        {"app_id": app_id},
    )
    # App-Level Token 需要在 App Settings 页面手动生成
    # apps.manifest API 暂不支持自动生成 xapp- token
    return result.get("token")


# ─── Output Helpers ──────────────────────────────────────────────────────────

def print_manifest_json(manifest: dict) -> None:
    """Print the manifest JSON for manual use."""
    print("\n" + "=" * 60)
    print("SLACK APP MANIFEST (复制下方 JSON 到 api.slack.com)")
    print("=" * 60)
    print(json.dumps(manifest, indent=2, ensure_ascii=False))
    print("=" * 60)
    print()


def print_manual_steps() -> None:
    """Print step-by-step manual creation guide."""
    print("""
手动创建步骤 (粘贴 Manifest):
──────────────────────────────
1. 打开 https://api.slack.com/apps
2. 点击 "Create New App"
3. 选择 "From an app manifest"
4. 选择目标 Workspace
5. 将上方 JSON 粘贴进去 → Next → Create
6. 进入 App → "OAuth & Permissions" → "Install to Workspace"
7. 同意授权, 复制 Bot User OAuth Token (xoxb-...)
8. 进入 "Basic Information" → "App-Level Tokens"
   → 点 "Generate Token and Scopes"
   → 名称: "socket-mode", Scope: connections:write
   → 复制 App Token (xapp-...)
9. 将两个 token 填入 .env:
   SLACK_BOT_TOKEN=xoxb-...
   SLACK_APP_TOKEN=xapp-...
""")


def print_api_result_guide(result: dict) -> None:
    """Print post-creation instructions after successful API call."""
    app_id = result.get("app_id", "N/A")
    credentials = result.get("credentials", {})
    bot_token = credentials.get("bot_user_oauth_token", "")
    verification_token = credentials.get("verification_token", "")

    print(f"""
✅ App 创建成功!
────────────────
App ID:            {app_id}
Bot Token (xoxb):  {bot_token or "见下方步骤"}
Verification Token: {verification_token or "N/A"}

⚠️  App-Level Token (xapp-) 需要手动生成:
   1. 打开 https://api.slack.com/apps/{app_id}/general
   2. 滚动到 "App-Level Tokens" 区域
   3. 点 "Generate Token and Scopes"
   4. Name: socket-mode, Scope: connections:write
   5. 复制 xapp-... token

安装 App 到 Workspace:
   https://api.slack.com/apps/{app_id}/install-on-team

之后更新 .env:
   SLACK_BOT_TOKEN={bot_token or 'xoxb-你的token'}
   SLACK_APP_TOKEN=xapp-你的token
""")


def save_manifest_to_file(manifest: dict, output_path: Path) -> None:
    """Save manifest JSON to a file."""
    output_path.write_text(json.dumps(manifest, indent=2, ensure_ascii=False))
    print(f"→ Manifest 已保存到: {output_path}")


# ─── Main ────────────────────────────────────────────────────────────────────

def build_arg_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        description="Butler Health Bot - Slack App Setup Script",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=__doc__,
    )
    parser.add_argument(
        "--config-token",
        metavar="TOKEN",
        help="Slack Configuration Token (xoxe-...). 有此参数时直接调 API 创建 App。",
    )
    parser.add_argument(
        "--print-manifest",
        action="store_true",
        help="只打印 Manifest JSON, 不做任何 API 调用。",
    )
    parser.add_argument(
        "--save-manifest",
        metavar="PATH",
        help="将 Manifest JSON 保存到指定文件路径。",
    )
    parser.add_argument(
        "--app-name",
        default="Butler Health Bot",
        help="App 显示名称 (默认: Butler Health Bot)",
    )
    return parser


def main() -> None:
    parser = build_arg_parser()
    args = parser.parse_args()

    # 允许自定义 app 名称
    manifest = dict(HEALTH_BOT_MANIFEST)
    manifest["display_information"] = dict(manifest["display_information"])
    manifest["display_information"]["name"] = args.app_name

    # 保存到文件
    if args.save_manifest:
        save_manifest_to_file(manifest, Path(args.save_manifest))

    # 只打印 Manifest
    if args.print_manifest or not args.config_token:
        print_manifest_json(manifest)
        if not args.save_manifest:
            print_manual_steps()
        return

    # 使用 Configuration Token 自动创建
    print(f"\n Butler Health Bot Setup")
    print(f"{'─' * 40}")
    print(f"App Name:  {args.app_name}")
    print(f"Token:     {args.config_token[:12]}...")
    print()

    result = create_app(args.config_token, manifest)

    if not result.get("ok"):
        error = result.get("error", "unknown_error")
        detail = result.get("response_metadata", {}).get("messages", [])
        print(f"[ERROR] API 返回失败: {error}", file=sys.stderr)
        if detail:
            for msg in detail:
                print(f"  → {msg}", file=sys.stderr)
        print("\n提示: 请确认 Token 格式正确 (xoxe-...), 且有创建 App 的权限。", file=sys.stderr)
        sys.exit(1)

    print_api_result_guide(result)


if __name__ == "__main__":
    main()
