#!/usr/bin/env python3
"""
飞书通知模块 - CI 关键事件推送

支持通知类型:
  - branch_failure: main/develop 分支 CI 失败
  - branch_recovery: main/develop 分支 CI 从失败恢复（绿色恢复）
  - e2e_failure: E2E 测试失败摘要
  - pr_failure: PR CI 失败（可选）

用法:
  # 命令行直接调用（供 CI workflow 使用）
  python3 -m scripts.ci.chatops.feishu_notify --mode failure --run-id 12345
  python3 scripts/ci/chatops/feishu_notify.py --mode recovery --run-id 12345

  # Python 模块调用
  from scripts.ci.chatops.feishu_notify import FeishuNotifier
  notifier = FeishuNotifier()
  notifier.notify_branch_failure(run_id=12345, branch="develop")

设计原则:
  1. 通知失败永远不阻断主流程（返回 0）
  2. 卡片信息丰富，一键跳转 Gitea 详情页
  3. 失败通知包含错误摘要，不用点进去就能判断严重程度
"""

import argparse
import json
import sys
import urllib.request
from datetime import datetime

from . import config
from .gitea_client import GiteaClient


class FeishuNotifier:
    """飞书通知发送器"""

    def __init__(self, webhook_url=None, gitea_client=None):
        self.webhook_url = webhook_url or config.FEISHU_WEBHOOK_URL
        self.gitea = gitea_client or GiteaClient()

    def _send_card(self, card_payload):
        """发送飞书卡片消息

        Returns:
            True 表示发送成功（飞书返回 code=0）
        """
        if not self.webhook_url:
            print("[INFO] 未配置 FEISHU_WEBHOOK_URL，跳过飞书通知")
            return False

        payload = {"msg_type": "interactive", "card": card_payload}
        data = json.dumps(payload).encode("utf-8")
        req = urllib.request.Request(
            self.webhook_url,
            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")
            result = json.loads(resp_body)
            if result.get("code", 0) != 0:
                print(
                    f"[WARN] 飞书通知返回错误: {result.get('msg', resp_body)}",
                    file=sys.stderr,
                )
                return False
            return True
        except Exception as e:
            print(f"[WARN] 飞书通知发送失败: {e}", file=sys.stderr)
            return False

    # ── 通知模板 ──────────────────────────────────────

    def _run_url(self, run_id):
        return f"{config.GITEA_URL}/{config.GITEA_REPO}/actions/runs/{run_id}"

    def _pr_url(self, pr_number):
        return f"{config.GITEA_URL}/{config.GITEA_REPO}/pulls/{pr_number}"

    def notify_branch_failure(self, run_id, branch, run_data=None):
        """main/develop 分支 CI 失败通知

        包含: 失败 job 列表、错误摘要、一键重跑链接
        """
        run = run_data or self.gitea.get_run(run_id)
        if not run:
            print(f"[WARN] 无法获取 run {run_id} 详情", file=sys.stderr)
            return False

        workflow_name = run.get("name", "Unknown Workflow")
        commit_msg = run.get("head_commit", {}).get("message", "未知").splitlines()[0][:60]
        commit_sha = run.get("head_sha", "")[:8]
        actor = (
            run.get("trigger_event", {}).get("actor", {}).get("login", "unknown")
            if isinstance(run.get("trigger_event"), dict)
            else run.get("actor", "unknown")
        )
        run_url = self._run_url(run_id)

        # 获取失败 job 摘要
        failed_jobs = self.gitea.get_failed_jobs_summary(run_id, max_lines_per_job=15)

        # 构建失败摘要
        failure_summary = ""
        if failed_jobs:
            job_lines = []
            for job in failed_jobs[:3]:  # 最多显示 3 个
                step_info = f"（{job['failed_step']}）" if job["failed_step"] else ""
                job_lines.append(f"• **{job['name']}**{step_info}")
                if job["log_tail"]:
                    # 取最后 3 行日志
                    tail_lines = job["log_tail"].strip().splitlines()[-3:]
                    for line in tail_lines:
                        clean_line = line.strip()[:100]
                        if clean_line:
                            job_lines.append(f"  `{clean_line}`")
            failure_summary = "\n".join(job_lines)
        else:
            failure_summary = "（获取失败详情中，点击查看日志）"

        # 字段
        fields = [
            {"is_short": True, "text": {"tag": "lark_md", "content": f"**分支**\n{branch}"}},
            {"is_short": True, "text": {"tag": "lark_md", "content": f"**Workflow**\n{workflow_name}"}},
            {"is_short": True, "text": {"tag": "lark_md", "content": f"**提交**\n`{commit_sha}`"}},
            {"is_short": True, "text": {"tag": "lark_md", "content": f"**触发者**\n{actor}"}},
            {"is_short": False, "text": {"tag": "lark_md", "content": f"**提交信息**\n{commit_msg}"}},
            {"is_short": False, "text": {"tag": "lark_md", "content": f"**失败详情**\n{failure_summary}"}},
        ]

        card = {
            "header": {
                "title": {
                    "tag": "plain_text",
                    "content": f"❌ CI告警：{branch} 分支构建失败",
                },
                "status": "red",
            },
            "elements": [
                {"tag": "div", "fields": fields},
                {
                    "tag": "action",
                    "actions": [
                        {
                            "tag": "button",
                            "text": {"tag": "plain_text", "content": "查看失败日志"},
                            "url": run_url,
                            "type": "danger",
                        },
                        {
                            "tag": "button",
                            "text": {"tag": "plain_text", "content": "重跑失败Job"},
                            "url": f"{run_url}/rerun-failed-jobs",
                            "type": "default",
                        },
                    ],
                },
            ],
        }

        result = self._send_card(card)
        print(f"[INFO] 分支失败通知已发送: {branch} run={run_id}")
        return result

    def notify_branch_recovery(self, run_id, branch, previous_failure_run_id=None):
        """分支 CI 恢复通知（从失败变成功）"""
        run = self.gitea.get_run(run_id)
        if not run:
            print(f"[WARN] 无法获取 run {run_id} 详情", file=sys.stderr)
            return False

        workflow_name = run.get("name", "Unknown Workflow")
        commit_sha = run.get("head_sha", "")[:8]
        run_url = self._run_url(run_id)

        # 计算恢复耗时
        duration_text = "已恢复"
        if previous_failure_run_id:
            prev_run = self.gitea.get_run(previous_failure_run_id)
            if prev_run:
                # 简单计算两个 run 的时间差
                prev_time = prev_run.get("created_at", "")
                cur_time = run.get("created_at", "")
                if prev_time and cur_time:
                    try:
                        t1 = datetime.fromisoformat(prev_time.replace("Z", "+00:00"))
                        t2 = datetime.fromisoformat(cur_time.replace("Z", "+00:00"))
                        diff = (t2 - t1).total_seconds() / 60
                        duration_text = f"故障时长约 {diff:.0f} 分钟"
                    except Exception:
                        pass

        fields = [
            {"is_short": True, "text": {"tag": "lark_md", "content": f"**分支**\n{branch}"}},
            {"is_short": True, "text": {"tag": "lark_md", "content": f"**Workflow**\n{workflow_name}"}},
            {"is_short": True, "text": {"tag": "lark_md", "content": f"**提交**\n`{commit_sha}`"}},
            {"is_short": True, "text": {"tag": "lark_md", "content": "**状态**\n✅ 已恢复"}},
            {"is_short": False, "text": {"tag": "lark_md", "content": f"**说明**\n{duration_text}"}},
        ]

        card = {
            "header": {
                "title": {
                    "tag": "plain_text",
                    "content": f"✅ CI通知：{branch} 分支构建已恢复",
                },
                "status": "green",
            },
            "elements": [
                {"tag": "div", "fields": fields},
                {
                    "tag": "action",
                    "actions": [
                        {
                            "tag": "button",
                            "text": {"tag": "plain_text", "content": "查看详情"},
                            "url": run_url,
                            "type": "primary",
                        }
                    ],
                },
            ],
        }

        result = self._send_card(card)
        print(f"[INFO] 分支恢复通知已发送: {branch} run={run_id}")
        return result

    def notify_e2e_failure(self, run_id, branch="develop", pr_number=None):
        """E2E 测试失败摘要通知"""
        failed_jobs = self.gitea.get_failed_jobs_summary(run_id, max_lines_per_job=50)
        e2e_jobs = [j for j in failed_jobs if "e2e" in j["name"].lower() or "test" in j["name"].lower()]

        if not e2e_jobs:
            # 没有明确的 e2e job，取所有失败的
            e2e_jobs = failed_jobs

        run_url = self._run_url(run_id)
        title_suffix = f"PR #{pr_number}" if pr_number else f"{branch} 分支"

        # 构建失败用例摘要
        case_summary = ""
        for job in e2e_jobs[:3]:
            case_summary += f"**{job['name']}**\n"
            if job["log_tail"]:
                # 尝试提取 FAIL 行
                fail_lines = [
                    line.strip()
                    for line in job["log_tail"].splitlines()
                    if "FAIL" in line or "fail" in line.lower() or "✗" in line or "●" in line
                ][:5]
                if fail_lines:
                    for line in fail_lines:
                        case_summary += f"  • {line[:120]}\n"
                else:
                    tail = job["log_tail"].strip().splitlines()[-5:]
                    for line in tail:
                        case_summary += f"  `{line.strip()[:100]}`\n"
            case_summary += "\n"

        if not case_summary:
            case_summary = "（点击查看完整测试报告）"

        fields = [
            {"is_short": True, "text": {"tag": "lark_md", "content": f"**来源**\n{title_suffix}"}},
            {"is_short": True, "text": {"tag": "lark_md", "content": f"**失败Job数**\n{len(e2e_jobs)}"}},
            {"is_short": False, "text": {"tag": "lark_md", "content": f"**失败摘要**\n{case_summary}"}},
        ]

        card = {
            "header": {
                "title": {
                    "tag": "plain_text",
                    "content": f"🧪 CI告警：E2E 测试失败 - {title_suffix}",
                },
                "status": "orange",
            },
            "elements": [
                {"tag": "div", "fields": fields},
                {
                    "tag": "action",
                    "actions": [
                        {
                            "tag": "button",
                            "text": {"tag": "plain_text", "content": "查看完整报告"},
                            "url": run_url,
                            "type": "danger",
                        }
                    ],
                },
            ],
        }

        result = self._send_card(card)
        print(f"[INFO] E2E失败通知已发送: run={run_id}")
        return result

    def notify_pr_failure(self, run_id, pr_number, pr_title=""):
        """PR CI 失败通知（轻量版，可选开启）"""
        run_url = self._run_url(run_id)
        pr_url = self._pr_url(pr_number)

        failed_jobs = self.gitea.get_failed_jobs_summary(run_id, max_lines_per_job=10)
        failure_names = [j["name"] for j in failed_jobs[:3]]
        failure_text = "、".join(failure_names) if failure_names else "未知"

        fields = [
            {
                "is_short": False,
                "text": {"tag": "lark_md", "content": f"**PR**\n[#{pr_number} {pr_title[:50]}]({pr_url})"},
            },
            {"is_short": False, "text": {"tag": "lark_md", "content": f"**失败任务**\n{failure_text}"}},
        ]

        card = {
            "header": {
                "title": {
                    "tag": "plain_text",
                    "content": f"⚠️ CI通知：PR #{pr_number} 构建失败",
                },
                "status": "yellow",
            },
            "elements": [
                {"tag": "div", "fields": fields},
                {
                    "tag": "action",
                    "actions": [
                        {
                            "tag": "button",
                            "text": {"tag": "plain_text", "content": "查看日志"},
                            "url": run_url,
                            "type": "default",
                        },
                        {
                            "tag": "button",
                            "text": {"tag": "plain_text", "content": "查看PR"},
                            "url": pr_url,
                            "type": "default",
                        },
                    ],
                },
            ],
        }

        result = self._send_card(card)
        print(f"[INFO] PR失败通知已发送: PR #{pr_number} run={run_id}")
        return result


# ── CLI 入口 ──────────────────────────────────────────


def main():
    parser = argparse.ArgumentParser(description="飞书 CI 通知")
    parser.add_argument(
        "--mode",
        required=True,
        choices=["failure", "recovery", "e2e_failure", "pr_failure"],
        help="通知模式",
    )
    parser.add_argument("--run-id", required=True, help="Workflow Run ID")
    parser.add_argument("--branch", default="develop", help="分支名")
    parser.add_argument("--pr-number", type=int, help="PR 编号")
    parser.add_argument("--pr-title", default="", help="PR 标题")
    parser.add_argument("--prev-run-id", help="上一个失败的 run ID（恢复通知用）")
    parser.add_argument("--webhook", help="飞书 webhook URL（覆盖环境变量）")

    args = parser.parse_args()

    notifier = FeishuNotifier(webhook_url=args.webhook)

    if args.mode == "failure":
        notifier.notify_branch_failure(args.run_id, args.branch)
    elif args.mode == "recovery":
        notifier.notify_branch_recovery(args.run_id, args.branch, previous_failure_run_id=args.prev_run_id)
    elif args.mode == "e2e_failure":
        notifier.notify_e2e_failure(args.run_id, branch=args.branch, pr_number=args.pr_number)
    elif args.mode == "pr_failure":
        notifier.notify_pr_failure(args.run_id, args.pr_number, pr_title=args.pr_title)

    return 0


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