#!/usr/bin/env python3
"""
统一CI通知脚本 - 发送飞书卡片通知
支持三种模式: start / success / failure
包含: PR链接、耗时、失败阶段、分支、提交者、Run链接、Runner信息

用法:
  NOTIFY_MODE=start   JOB_NAME="xxx" python3 scripts/ci_notify.py
  NOTIFY_MODE=success JOB_NAME="xxx" JOB_DURATION="2m30s" python3 scripts/ci_notify.py
  NOTIFY_MODE=failure JOB_NAME="xxx" FAILED_STEP="xxx" JOB_DURATION="2m30s" python3 scripts/ci_notify.py

环境变量:
  CI_NOTIFY_WEBHOOK - 飞书webhook地址 (必填)
  NOTIFY_MODE       - 通知模式: start / success / failure (必填)
  JOB_NAME          - Job名称 (必填)
  JOB_DURATION      - 耗时，如"2m30s" (成功/失败时建议传)
  FAILED_STEP       - 失败的步骤名 (失败时建议传)
  GITHUB_REF_NAME   - 分支名
  GITHUB_SHA        - commit SHA
  GITHUB_ACTOR      - 提交者
  GITHUB_RUN_ID     - Run ID
  GITHUB_REPOSITORY - 仓库路径
  GITHUB_EVENT_NAME - 事件类型 (pull_request / push / ...)
  GITHUB_PR_NUMBER  - PR编号 (PR事件时)
  GITHUB_PR_TITLE   - PR标题 (PR事件时)
  RUNNER_NAME       - Runner名称 (可选，自动获取)

设计原则:
  1. 通知失败永远不阻断CI主流程（返回exit code 0）
  2. 标题包含"CI通知"/"CI告警"关键词，适配飞书webhook关键词校验
  3. 卡片信息尽量丰富，方便快速定位问题
"""

import json
import os
import sys
import urllib.request


def get_env(name, default=""):
    """读取环境变量"""
    return os.environ.get(name, default)


def format_duration(seconds_str):
    """将秒数格式化为易读形式"""
    try:
        seconds = int(float(seconds_str))
        mins = seconds // 60
        secs = seconds % 60
        if mins > 0:
            return f"{mins}m{secs}s"
        return f"{secs}s"
    except (ValueError, TypeError):
        return seconds_str or "未知"


def classify_job(job_name):
    """根据Job名称判断所属阶段"""
    name = job_name.lower()
    if any(k in name for k in ["validate", "lint", "unit test", "integration test"]):
        return "门禁检查"
    if any(k in name for k in ["build", "image"]):
        return "镜像构建"
    if any(k in name for k in ["deploy", "staging", "production"]):
        return "部署发布"
    if any(k in name for k in ["e2e", "test", "smoke"]):
        return "测试验证"
    return "其他"


def main() -> int:
    webhook = get_env("CI_NOTIFY_WEBHOOK")
    if not webhook:
        print("未配置 CI_NOTIFY_WEBHOOK，跳过通知")
        print("如需启用，请在仓库 Settings -> Secrets and variables -> Actions 中添加 CI_NOTIFY_WEBHOOK")
        return 0

    mode = get_env("NOTIFY_MODE", "failure").lower()
    job_name = get_env("JOB_NAME", "Unknown Job")
    duration = get_env("JOB_DURATION")
    if not duration:
        duration_sec = get_env("JOB_DURATION_SECONDS")
        duration = format_duration(duration_sec) if duration_sec else "计算中..."

    failed_step = get_env("FAILED_STEP", "")
    branch = get_env("GITHUB_REF_NAME", "unknown")
    commit = get_env("GITHUB_SHA", "unknown")[:8]
    actor = get_env("GITHUB_ACTOR", "unknown")
    run_id = get_env("GITHUB_RUN_ID", "unknown")
    repo = get_env("GITHUB_REPOSITORY", "unknown")
    event_name = get_env("GITHUB_EVENT_NAME", "")
    pr_number = get_env("GITHUB_PR_NUMBER", "")
    pr_title = get_env("GITHUB_PR_TITLE", "")
    runner_name = get_env("RUNNER_NAME", "")

    run_url = f"https://git.xiaoxiajianji.com/{repo}/actions/runs/{run_id}"
    job_stage = classify_job(job_name)

    # 根据模式设置标题、状态、颜色
    # 注意：标题中必须包含飞书webhook配置的关键词，否则会报"Key Words Not Found"
    # 这里加入"CI通知"/"CI告警"关键词提高命中率
    if mode == "start":
        title = f"🔄 CI通知：{job_name} 开始构建"
        status = "blue"
        button_text = "查看进度"
        button_type = "primary"
    elif mode == "success":
        title = f"✅ CI通知：{job_name} 构建成功"
        status = "green"
        button_text = "查看详情"
        button_type = "primary"
    else:  # failure
        title = f"❌ CI告警：{job_name} 构建失败"
        status = "red"
        button_text = "查看失败日志"
        button_type = "danger"

    # 构建卡片内容 - 左侧标签+右侧值的结构化展示
    fields = []

    fields.append({"is_short": True, "text": {"tag": "lark_md", "content": f"**阶段**\n{job_stage}"}})
    fields.append({"is_short": True, "text": {"tag": "lark_md", "content": f"**任务**\n{job_name}"}})

    if mode != "start":
        fields.append({"is_short": True, "text": {"tag": "lark_md", "content": f"**耗时**\n{duration}"}})
    else:
        fields.append({"is_short": True, "text": {"tag": "lark_md", "content": "**状态**\n进行中"}})

    if runner_name:
        fields.append({"is_short": True, "text": {"tag": "lark_md", "content": f"**Runner**\n{runner_name}"}})

    if mode == "failure" and failed_step:
        fields.append({"is_short": False, "text": {"tag": "lark_md", "content": f"**失败步骤**\n{failed_step}"}})

    # PR/分支信息
    if event_name == "pull_request" and pr_number:
        pr_url = f"https://git.xiaoxiajianji.com/{repo}/pulls/{pr_number}"
        pr_display = f"#{pr_number}"
        if pr_title:
            pr_display += f" {pr_title[:30]}"
        fields.append({"is_short": False, "text": {"tag": "lark_md", "content": f"**PR**\n[{pr_display}]({pr_url})"}})
    elif event_name == "push":
        fields.append({"is_short": True, "text": {"tag": "lark_md", "content": f"**分支**\n{branch}"}})

    fields.append({"is_short": True, "text": {"tag": "lark_md", "content": f"**提交**\n`{commit}`"}})
    fields.append({"is_short": True, "text": {"tag": "lark_md", "content": f"**提交者**\n{actor}"}})
    fields.append({"is_short": True, "text": {"tag": "lark_md", "content": f"**Run ID**\n{run_id}"}})

    payload = {
        "msg_type": "interactive",
        "card": {
            "header": {
                "title": {
                    "tag": "plain_text",
                    "content": title,
                },
                "status": status,
            },
            "elements": [
                {
                    "tag": "div",
                    "fields": fields,
                },
                {
                    "tag": "action",
                    "actions": [
                        {
                            "tag": "button",
                            "text": {"tag": "plain_text", "content": button_text},
                            "url": run_url,
                            "type": button_type,
                        }
                    ],
                },
            ],
        },
    }

    data = json.dumps(payload).encode("utf-8")
    req = urllib.request.Request(
        webhook,
        data=data,
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    try:
        with urllib.request.urlopen(req, timeout=10) as resp:
            resp_body = resp.read().decode("utf-8")
        # 飞书返回code=0表示成功
        try:
            result = json.loads(resp_body)
            if result.get("code", 0) != 0:
                print(f"通知发送告警: 飞书返回错误 - {result.get('msg', resp_body)}", file=sys.stderr)
                print(f"通知已发送 ({mode}) - 飞书返回非0，但不阻断CI流程")
            else:
                print(f"通知已发送 ({mode})")
        except json.JSONDecodeError:
            print(f"通知已发送 ({mode})")
    except Exception as e:
        print(f"通知发送告警: {e}", file=sys.stderr)

    # 通知无论成功失败都不阻断CI主流程，统一返回0
    return 0


if __name__ == "__main__":
    sys.exit(main())
