"""
预警通知推送模块 — 管理会计OS
支持渠道：企业微信 (群机器人/应用消息)、邮件
"""

import json
import logging
import urllib.request
import urllib.error
import smtplib
from email.mime.text import MIMEText
from email.header import Header
from datetime import datetime
from typing import Optional

logger = logging.getLogger("cma.notifier")

# ============================================================
# 企业微信机器人推送
# ============================================================

def send_wecom_robot(webhook_url: str, title: str, content: str, alert_level: str = "yellow") -> dict:
    """通过企业微信群机器人发送告警"""
    color_tag = {"red": "🔴", "yellow": "🟡", "green": "🟢"}.get(alert_level, "⚪")
    msg = {
        "msgtype": "markdown",
        "markdown": {
            "content": f"## {color_tag} 管理会计OS预警通知\n"
                       f"**{title}**\n\n"
                       f"{content}\n\n"
                       f"---\n"
                       f"⏰ {datetime.now().strftime('%Y-%m-%d %H:%M')}"
        }
    }
    data = json.dumps(msg).encode("utf-8")
    req = urllib.request.Request(webhook_url, data=data,
                                 headers={"Content-Type": "application/json"})
    try:
        with urllib.request.urlopen(req, timeout=10) as resp:
            result = json.loads(resp.read().decode())
            if result.get("errcode") == 0:
                return {"success": True, "message": "已推送至企业微信群"}
            else:
                return {"success": False, "message": f"推送失败: {result.get('errmsg', '未知错误')}"}
    except Exception as e:
        return {"success": False, "message": f"推送异常: {str(e)}"}


# ============================================================
# 企业微信应用消息推送（通过自建应用 message/send API）
# ============================================================

def send_wecom_app(corp_id: str, corp_secret: str, agent_id: str,
                   touser: str, title: str, content: str, alert_level: str = "yellow") -> dict:
    """通过企微自建应用发送应用消息"""
    import requests
    try:
        token_url = f"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={corp_id}&corpsecret={corp_secret}"
        r = requests.get(token_url, timeout=10)
        token_data = r.json()
        if token_data.get("errcode") != 0:
            return {"success": False, "message": f"获取token失败: {token_data.get('errmsg', '')}"}
        access_token = token_data["access_token"]

        color_tag = {"red": "🔴", "yellow": "🟡", "green": "🟢"}.get(alert_level, "⚪")
        md = "## " + color_tag + " 管理会计OS预警通知\n\n"
        md += "**" + title + "**\n\n"
        md += content + "\n\n---\n"
        md += "⏰ " + datetime.now().strftime("%Y-%m-%d %H:%M")

        payload = {
            "touser": touser,
            "msgtype": "markdown",
            "agentid": int(agent_id),
            "markdown": {"content": md},
            "safe": 0,
        }
        send_url = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + access_token
        r2 = requests.post(send_url, json=payload, timeout=10)
        send_data = r2.json()
        if send_data.get("errcode") == 0:
            return {"success": True, "message": f"已推送到企微用户 {touser}"}
        else:
            return {"success": False, "message": f"推送失败: {send_data.get('errmsg', '')}"}
    except Exception as e:
        return {"success": False, "message": f"推送异常: {str(e)}"}


# ============================================================
# 邮件推送
# ============================================================

def send_mail(smtp_config: dict, to_addrs: list, title: str, content: str) -> dict:
    """通过 SMTP 发送邮件告警"""
    try:
        msg = MIMEText(content, "plain", "utf-8")
        msg["Subject"] = Header(f"[管理会计OS预警] {title}", "utf-8")
        msg["From"] = smtp_config.get("from_addr", "")
        msg["To"] = ", ".join(to_addrs)

        host = smtp_config.get("host", "smtp.qq.com")
        port = int(smtp_config.get("port", 465))
        user = smtp_config.get("user", "")
        password = smtp_config.get("password", "")
        use_ssl = smtp_config.get("use_ssl", True)

        if use_ssl:
            server = smtplib.SMTP_SSL(host, port, timeout=10)
        else:
            server = smtplib.SMTP(host, port, timeout=10)
            server.starttls()

        server.login(user, password)
        server.sendmail(user, to_addrs, msg.as_string())
        server.quit()
        return {"success": True, "message": f"已发送邮件至 {', '.join(to_addrs)}"}
    except Exception as e:
        return {"success": False, "message": f"邮件发送失败: {str(e)}"}


# ============================================================
# 主推送函数
# ============================================================

def send_wecom_message(content: str, title: str = "管理会计OS通知", alert_level: str = "green") -> dict:
    """发送企业微信消息到所有已启用的通知渠道（验证结果/行动报告推送用）

    - 读取 notification_channels 表中启用的 wecom_app / wecom 渠道
    - 无渠道时记日志并返回失败（不抛异常，保证主流程不中断）
    """
    try:
        from app.database import get_session_local
        from app.models import NotificationChannel
    except Exception as e:
        logger.warning(f"send_wecom_message: 加载依赖失败: {e}")
        return {"success": False, "message": f"依赖加载失败: {e}"}

    db = get_session_local()()
    try:
        channels = db.query(NotificationChannel).filter(
            NotificationChannel.enabled == True,
            NotificationChannel.channel_type.in_(["wecom_app", "wecom"]),
        ).all()
        if not channels:
            logger.info(f"send_wecom_message: 无已启用的企微渠道，跳过推送: {title}")
            return {"success": False, "message": "无已启用的企微通知渠道"}

        results = []
        for ch in channels:
            config = ch.config or {}
            if ch.channel_type == "wecom_app":
                r = send_wecom_app(
                    corp_id=config.get("corp_id", ""),
                    corp_secret=config.get("corp_secret", ""),
                    agent_id=config.get("agent_id", ""),
                    touser=config.get("touser", "@all"),
                    title=title, content=content, alert_level=alert_level,
                )
            else:
                r = send_wecom_robot(
                    webhook_url=config.get("webhook_url", ""),
                    title=title, content=content, alert_level=alert_level,
                )
            r["channel"] = ch.channel_type
            results.append(r)
        return {
            "success": any(r.get("success") for r in results),
            "results": results,
        }
    except Exception as e:
        logger.error(f"send_wecom_message 异常: {e}")
        return {"success": False, "message": f"推送异常: {e}"}
    finally:
        db.close()


def push_alert(alert: dict, channels: list[dict]) -> list[dict]:
    """向所有已启用渠道推送一条预警"""
    results = []
    alert_level = alert.get("alert_level", "yellow")
    title = alert.get("alert_message", "预警通知")
    content = _build_content(alert)

    for ch in channels:
        if not ch.get("enabled", True):
            continue

        ch_type = ch.get("channel_type", "")
        config = ch.get("config", {})
        result = {"channel": ch_type, "channel_name": ch.get("name", ""), "success": False}

        if ch_type == "wecom":
            webhook = config.get("webhook_url", "")
            if webhook:
                result = send_wecom_robot(webhook, title, content, alert_level)
                result["channel"] = "wecom"

        elif ch_type == "wecom_app":
            result = send_wecom_app(
                corp_id=config.get("corp_id", ""),
                corp_secret=config.get("corp_secret", ""),
                agent_id=config.get("agent_id", ""),
                touser=config.get("touser", ""),
                title=title, content=content, alert_level=alert_level,
            )
            result["channel"] = "wecom_app"

        elif ch_type == "mail":
            to_list = config.get("to", [])
            if to_list:
                result = send_mail(config, to_list, title, content)
                result["channel"] = "mail"

        results.append({**result, "channel_name": ch.get("name", "")})

    return results


def _build_content(alert: dict) -> str:
    """构建预警详情内容"""
    parts = [
        f"KPI: {alert.get('kpi_name', '未知')}",
        f"期间: {alert.get('period', '')}",
        f"实际值: {alert.get('actual_value', '-')}",
        f"目标值: {alert.get('target_value', '-')}",
        f"预警级别: {'🔴 紧急' if alert.get('alert_level') == 'red' else '🟡 警告'}",
    ]
    if alert.get("resolution"):
        parts.append(f"处理建议: {alert['resolution']}")
    return "\n".join(parts)


# ============================================================
# 从数据库加载渠道配置并推送待处理预警
# ============================================================

def push_pending_alerts(db_session) -> int:
    """推送所有待处理预警"""
    from app.models import NotificationChannel, NotificationLog, KPIAlert, KPIDefinition, KPIValue

    # 加载已启用的通知渠道
    channels = db_session.query(NotificationChannel).filter(
        NotificationChannel.enabled == True
    ).all()

    if not channels:
        logger.info("无已启用的通知渠道，跳过推送")
        return 0

    # 查待处理的预警
    alerts = db_session.query(KPIAlert).filter(
        KPIAlert.status == "pending"
    ).all()

    if not alerts:
        logger.info("无待处理预警")
        return 0

    channel_configs = [json.loads(json.dumps({
        "name": c.name, "channel_type": c.channel_type,
        "config": c.config, "enabled": c.enabled
    })) for c in channels]

    pushed = 0
    for alert in alerts:
        # 获取预警详情
        kpi = db_session.query(KPIDefinition).filter(
            KPIDefinition.id == alert.kpi_id
        ).first()
        kpi_value = db_session.query(KPIValue).filter(
            KPIValue.id == alert.kpi_value_id
        ).first()

        alert_data = {
            "alert_level": alert.alert_level,
            "alert_message": alert.alert_message,
            "kpi_name": kpi.kpi_name if kpi else "未知",
            "period": kpi_value.period if kpi_value else "",
            "actual_value": kpi_value.actual_value if kpi_value else None,
            "target_value": kpi.target_value if kpi else None,
        }

        results = push_alert(alert_data, channel_configs)

        # 记录推送日志
        for r in results:
            log = NotificationLog(
                alert_id=alert.id,
                channel=r.get("channel", ""),
                recipient=r.get("channel_name", ""),
                title=alert.alert_message[:200],
                content=alert.alert_message,
                status="sent" if r.get("success") else "failed",
                error_msg=r.get("message") if not r.get("success") else None,
                sent_at=datetime.now(),
            )
            db_session.add(log)
            if r.get("success"):
                pushed += 1
                logger.info(f"  已推送预警 #{alert.id} -> {r.get('channel_name')}")

    db_session.commit()
    return pushed
