#!/usr/bin/env python3
"""
Telegram 通知模組（多頻道版 v2）
================================
提供統一的 Telegram 通知功能，支援多個頻道分類推送。

使用方式：
    from telegram_notifier import send_message, send_success, send_error
    from telegram_notifier import Channel, send_to_channel

環境變數：
    TELEGRAM_BOT_TOKEN          - Bot API Token（共用）
    TELEGRAM_BOT_TOKEN_DEV      - DEV 頻道專用 Bot Token（nkdeveloper）
    TELEGRAM_BOT_TOKEN_INVEST   - INVEST 頻道專用 Bot Token（wqassistant）
    TELEGRAM_BOT_TOKEN_BLOG     - BLOG 頻道專用 Bot Token（催稿貓）
    TELEGRAM_BOT_TOKEN_THESIS   - THESIS 頻道專用 Bot Token（畢業犬）
    TELEGRAM_CHAT_ID            - 預設頻道（HRE助手，工作相關）
    TELEGRAM_CHAT_ID_DAILY      - 生活頻道（天氣、記帳、餐廳、日誌）
    TELEGRAM_CHAT_ID_DEV        - 程式開發頻道（Git Commit、專案通知）
    TELEGRAM_CHAT_ID_RESEARCH   - 研究頻道（論文監控、研究 ideas）
    TELEGRAM_CHAT_ID_INVEST     - 投資頻道（股票警示、FinLab、重大訊息）
    TELEGRAM_CHAT_ID_BLOG       - 部落格頻道（催稿貓週報）
    TELEGRAM_CHAT_ID_THESIS     - 論文頻道（畢業犬進度報告）

頻道設計：
    Channel.WORK     - 工作相關（MOPS、估價師異動、客戶通知）
    Channel.DAILY    - 生活相關（天氣、記帳、餐廳、日誌、健康）
    Channel.DEV      - 程式開發（Git Commit、部署通知）← 使用獨立 bot
    Channel.RESEARCH - 學術研究（論文監控、研究 ideas）
    Channel.INVEST   - 投資監控（股票警示、FinLab、公司重大訊息）
    Channel.BLOG     - 部落格教練（催稿貓週報）← 使用獨立 bot
    Channel.THESIS   - 論文教練（畢業犬進度報告）← 使用獨立 bot

範例：
    # 基本訊息（發送到預設頻道）
    send_message("Hello, World!")

    # 發送到指定頻道
    send_to_channel(Channel.DAILY, "今日天氣晴朗！")

    # 成功通知
    send_success("任務完成", details={"處理數量": 100})

    # 天氣預報（發送到 DAILY）
    send_daily_weather(location="台北", weather="晴", ...)
"""

import sys
sys.stdout.reconfigure(encoding='utf-8')

import os
import requests
import logging
from typing import Optional, Dict, Any
from datetime import datetime
from enum import Enum

# ===== 頻道定義 =====
class Channel(Enum):
    """Telegram 頻道分類"""
    WORK = "work"          # 工作相關（MOPS、估價師異動）
    DAILY = "daily"        # 生活相關（天氣、記帳、餐廳、日誌）
    DEV = "dev"            # 程式開發（Git Commit、部署）
    RESEARCH = "research"  # 學術研究（論文監控、研究 ideas）
    INVEST = "invest"      # 投資監控（股票警示、FinLab）
    BLOG = "blog"          # 部落格教練（催稿貓）
    THESIS = "thesis"      # 論文教練（畢業犬）

# ===== 設定 =====
TELEGRAM_BOT_TOKEN = os.environ.get('TELEGRAM_BOT_TOKEN', '')
TELEGRAM_BOT_TOKEN_DEV = os.environ.get('TELEGRAM_BOT_TOKEN_DEV', '')
TELEGRAM_BOT_TOKEN_INVEST = os.environ.get('TELEGRAM_BOT_TOKEN_INVEST', '')
TELEGRAM_BOT_TOKEN_BLOG = os.environ.get('TELEGRAM_BOT_TOKEN_BLOG', '')
TELEGRAM_BOT_TOKEN_THESIS = os.environ.get('TELEGRAM_BOT_TOKEN_THESIS', '')

# 多頻道 Chat ID 對照表
CHANNEL_CHAT_IDS = {
    Channel.WORK: os.environ.get('TELEGRAM_CHAT_ID', ''),
    Channel.DAILY: os.environ.get('TELEGRAM_CHAT_ID_DAILY', ''),
    Channel.DEV: os.environ.get('TELEGRAM_CHAT_ID_DEV', ''),
    Channel.RESEARCH: os.environ.get('TELEGRAM_CHAT_ID_RESEARCH', ''),
    Channel.INVEST: os.environ.get('TELEGRAM_CHAT_ID_INVEST', ''),
    Channel.BLOG: os.environ.get('TELEGRAM_CHAT_ID_BLOG', ''),
    Channel.THESIS: os.environ.get('TELEGRAM_CHAT_ID_THESIS', ''),
}

# 頻道對應的 Bot Token（部分頻道使用獨立 bot）
CHANNEL_BOT_TOKENS = {
    Channel.WORK: TELEGRAM_BOT_TOKEN,
    Channel.DAILY: TELEGRAM_BOT_TOKEN,
    Channel.DEV: TELEGRAM_BOT_TOKEN_DEV or TELEGRAM_BOT_TOKEN,
    Channel.RESEARCH: TELEGRAM_BOT_TOKEN,
    Channel.INVEST: TELEGRAM_BOT_TOKEN_INVEST or TELEGRAM_BOT_TOKEN,
    Channel.BLOG: TELEGRAM_BOT_TOKEN_BLOG or TELEGRAM_BOT_TOKEN,
    Channel.THESIS: TELEGRAM_BOT_TOKEN_THESIS or TELEGRAM_BOT_TOKEN,
}

# 向後相容：預設 Chat ID
TELEGRAM_CHAT_ID = CHANNEL_CHAT_IDS[Channel.WORK]

logger = logging.getLogger(__name__)


def get_channel_config(channel: Channel = Channel.WORK) -> tuple:
    """取得指定頻道的 Bot Token 和 Chat ID

    Returns:
        (bot_token, chat_id) 元組
    """
    chat_id = CHANNEL_CHAT_IDS.get(channel, '')
    bot_token = CHANNEL_BOT_TOKENS.get(channel, TELEGRAM_BOT_TOKEN)

    if not chat_id:
        # 回退到預設頻道
        chat_id = CHANNEL_CHAT_IDS[Channel.WORK]
        bot_token = TELEGRAM_BOT_TOKEN
        if chat_id:
            logger.debug(f"頻道 {channel.value} 未設定，回退到預設頻道")

    return bot_token, chat_id


def get_chat_id(channel: Channel = Channel.WORK) -> str:
    """取得指定頻道的 Chat ID，若未設定則回退到預設頻道"""
    _, chat_id = get_channel_config(channel)
    return chat_id


def send_to_channel(
    channel: Channel,
    message: str,
    parse_mode: str = "Markdown",
    disable_notification: bool = False
) -> bool:
    """發送訊息到指定頻道

    Args:
        channel: 目標頻道
        message: 訊息內容
        parse_mode: 解析模式
        disable_notification: 是否靜音

    Returns:
        是否發送成功
    """
    bot_token, chat_id = get_channel_config(channel)
    if not bot_token or not chat_id:
        logger.warning(f"Telegram 設定不完整（頻道：{channel.value}）")
        return False

    try:
        url = f"https://api.telegram.org/bot{bot_token}/sendMessage"
        payload = {
            'chat_id': chat_id,
            'text': message,
            'parse_mode': parse_mode,
            'disable_notification': disable_notification
        }
        response = requests.post(url, json=payload, timeout=10)
        response.raise_for_status()
        logger.debug(f"[{channel.value}] 訊息已發送: {message[:50]}...")
        return True
    except Exception as e:
        logger.warning(f"[{channel.value}] Telegram 通知發送失敗: {e}")
        return False


def send_message(
    message: str,
    parse_mode: str = "Markdown",
    disable_notification: bool = False
) -> bool:
    """發送 Telegram 訊息

    Args:
        message: 訊息內容（支援 Markdown 或 HTML）
        parse_mode: 解析模式 ("Markdown" 或 "HTML")
        disable_notification: 是否靜音發送

    Returns:
        是否發送成功
    """
    if not TELEGRAM_BOT_TOKEN or not TELEGRAM_CHAT_ID:
        logger.warning("Telegram 設定不完整，請設定環境變數 TELEGRAM_BOT_TOKEN 和 TELEGRAM_CHAT_ID")
        return False

    try:
        url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage"
        payload = {
            'chat_id': TELEGRAM_CHAT_ID,
            'text': message,
            'parse_mode': parse_mode,
            'disable_notification': disable_notification
        }
        response = requests.post(url, json=payload, timeout=10)
        response.raise_for_status()
        logger.debug(f"Telegram 訊息已發送: {message[:50]}...")
        return True
    except Exception as e:
        logger.warning(f"Telegram 通知發送失敗: {e}")
        return False


def send_success(
    title: str,
    details: Optional[Dict[str, Any]] = None,
    footer: Optional[str] = None
) -> bool:
    """發送成功通知

    Args:
        title: 標題
        details: 詳細資訊（字典格式）
        footer: 底部備註

    Returns:
        是否發送成功
    """
    timestamp = datetime.now().strftime('%Y-%m-%d %H:%M')
    message = f"✅ *{title}*\n\n"
    message += f"📅 {timestamp}\n"

    if details:
        message += "\n"
        for key, value in details.items():
            message += f"• {key}：{value}\n"

    if footer:
        message += f"\n{'─' * 20}\n"
        message += f"_{footer}_"

    return send_message(message)


def send_error(
    title: str,
    error: Optional[str] = None,
    details: Optional[Dict[str, Any]] = None
) -> bool:
    """發送錯誤通知

    Args:
        title: 標題
        error: 錯誤訊息
        details: 詳細資訊（字典格式）

    Returns:
        是否發送成功
    """
    timestamp = datetime.now().strftime('%Y-%m-%d %H:%M')
    message = f"❌ *{title}*\n\n"
    message += f"📅 {timestamp}\n"

    if error:
        message += f"\n⚠️ 錯誤：`{error}`\n"

    if details:
        message += "\n"
        for key, value in details.items():
            message += f"• {key}：{value}\n"

    return send_message(message)


def send_info(
    title: str,
    content: str,
    details: Optional[Dict[str, Any]] = None
) -> bool:
    """發送資訊通知

    Args:
        title: 標題
        content: 內容
        details: 詳細資訊（字典格式）

    Returns:
        是否發送成功
    """
    timestamp = datetime.now().strftime('%Y-%m-%d %H:%M')
    message = f"ℹ️ *{title}*\n\n"
    message += f"📅 {timestamp}\n"
    message += f"\n{content}\n"

    if details:
        message += "\n"
        for key, value in details.items():
            message += f"• {key}：{value}\n"

    return send_message(message)


def send_commit_notification(
    repo_name: str,
    branch: str,
    commit_message: str,
    files_changed: int = 0,
    insertions: int = 0,
    deletions: int = 0
) -> bool:
    """發送 Git Commit 通知（發送到 DEV 頻道）

    Args:
        repo_name: 專案名稱
        branch: 分支名稱
        commit_message: Commit 訊息
        files_changed: 變更檔案數
        insertions: 新增行數
        deletions: 刪除行數

    Returns:
        是否發送成功
    """
    timestamp = datetime.now().strftime('%Y-%m-%d %H:%M')

    # 移除可能導致 Markdown 解析錯誤的特殊字元
    safe_commit_message = commit_message.replace('_', '\\_').replace('*', '\\*').replace('`', '\\`')
    safe_repo_name = repo_name.replace('_', '\\_')

    message = f"📝 *Git Commit*\n\n"
    message += f"📅 {timestamp}\n"
    message += f"📁 {safe_repo_name} ({branch})\n"
    message += f"\n{safe_commit_message}\n"

    if files_changed > 0:
        message += f"\n"
        message += f"📊 {files_changed} 檔案變更"
        if insertions > 0 or deletions > 0:
            message += f" (+{insertions}/-{deletions})"
        message += "\n"

    # 發送到 DEV 頻道（程式開發通知）
    return send_to_channel(Channel.DEV, message)


# ===== 生活相關通知（DAILY 頻道）=====

def send_earthquake_alert(
    magnitude: float,
    location: str,
    depth: float,
    time: str,
    report_content: Optional[str] = None
) -> bool:
    """發送地震通知到生活頻道

    Args:
        magnitude: 地震規模
        location: 震央位置
        depth: 震源深度（公里）
        time: 發生時間
        report_content: 報告摘要

    Returns:
        是否發送成功
    """
    # 根據規模決定緊急程度
    if magnitude >= 6.0:
        emoji = "🚨🚨🚨"
        level = "強烈地震"
    elif magnitude >= 5.0:
        emoji = "🚨🚨"
        level = "有感地震"
    elif magnitude >= 4.0:
        emoji = "🚨"
        level = "輕微地震"
    else:
        emoji = "📍"
        level = "小型地震"

    message = f"{emoji} *{level}*\n\n"
    message += f"🕐 {time}\n"
    message += f"📍 {location}\n"
    message += f"📊 規模 {magnitude}，深度 {depth} km\n"

    if report_content:
        message += f"\n_{report_content}_"

    return send_to_channel(Channel.DAILY, message)


def send_weather_alert(
    headline: str,
    hazard_type: str,
    affected_areas: str,
    start_time: str,
    end_time: str,
    content: Optional[str] = None
) -> bool:
    """發送氣象警特報到生活頻道

    Args:
        headline: 標題
        hazard_type: 災害類型
        affected_areas: 影響區域
        start_time: 生效時間
        end_time: 結束時間
        content: 詳細內容

    Returns:
        是否發送成功
    """
    message = f"⚠️ *{headline}*\n\n"
    message += f"🌀 類型：{hazard_type}\n"
    message += f"📍 區域：{affected_areas}\n"
    message += f"🕐 {start_time} ~ {end_time}\n"

    if content:
        # 截斷過長內容
        short_content = content[:200] + "..." if len(content) > 200 else content
        message += f"\n{short_content}"

    return send_to_channel(Channel.DAILY, message)


def send_aqi_alert(
    location: str,
    aqi: int,
    status: str,
    pm25: Optional[float] = None,
    suggestion: Optional[str] = None
) -> bool:
    """發送空氣品質警報到生活頻道

    Args:
        location: 地點
        aqi: AQI 指數
        status: 狀態（良好/普通/不健康等）
        pm25: PM2.5 數值
        suggestion: 建議

    Returns:
        是否發送成功
    """
    # 根據 AQI 選擇 emoji
    if aqi <= 50:
        emoji = "🟢"
    elif aqi <= 100:
        emoji = "🟡"
    elif aqi <= 150:
        emoji = "🟠"
    elif aqi <= 200:
        emoji = "🔴"
    else:
        emoji = "🟣"

    message = f"{emoji} *空氣品質通知*\n\n"
    message += f"📍 {location}\n"
    message += f"📊 AQI {aqi}（{status}）\n"

    if pm25 is not None:
        message += f"🌫️ PM2.5：{pm25} μg/m³\n"

    if suggestion:
        message += f"\n💡 {suggestion}"

    return send_to_channel(Channel.DAILY, message)


def send_daily_weather(
    location: str,
    weather: str,
    temp_min: int,
    temp_max: int,
    rain_prob: int,
    comfort: str,
    suggestion: Optional[str] = None,
    aqi: Optional[int] = None
) -> bool:
    """發送每日天氣摘要到每日頻道

    Args:
        location: 地點
        weather: 天氣狀況
        temp_min: 最低溫
        temp_max: 最高溫
        rain_prob: 降雨機率
        comfort: 舒適度
        suggestion: 穿搭建議
        aqi: 空氣品質指數

    Returns:
        是否發送成功
    """
    timestamp = datetime.now().strftime('%Y-%m-%d %H:%M')

    # 天氣 emoji
    weather_emoji = "☀️" if "晴" in weather else "🌤️" if "雲" in weather else "🌧️" if "雨" in weather else "⛅"

    message = f"🌅 *早安，凱文！*\n\n"
    message += f"📅 {timestamp}\n"
    message += f"📍 {location}\n\n"
    message += f"{weather_emoji} {weather}\n"
    message += f"🌡️ {temp_min}°C ~ {temp_max}°C\n"
    message += f"🌧️ 降雨機率 {rain_prob}%\n"
    message += f"😊 {comfort}\n"

    if aqi is not None:
        aqi_emoji = "🟢" if aqi <= 50 else "🟡" if aqi <= 100 else "🟠" if aqi <= 150 else "🔴"
        message += f"{aqi_emoji} AQI {aqi}\n"

    if suggestion:
        message += f"\n👔 *今日建議*\n{suggestion}"

    return send_to_channel(Channel.DAILY, message)


# ===== 研究相關通知（RESEARCH 頻道）=====

def send_paper_notification(
    title: str,
    authors: str,
    journal: str,
    keywords: Optional[list] = None,
    abstract: Optional[str] = None,
    url: Optional[str] = None
) -> bool:
    """發送新論文通知到研究頻道

    Args:
        title: 論文標題
        authors: 作者
        journal: 期刊名稱
        keywords: 關鍵字列表
        abstract: 摘要（會截斷）
        url: 論文連結

    Returns:
        是否發送成功
    """
    message = f"📚 *新論文通知*\n\n"
    message += f"📄 {title}\n"
    message += f"👤 {authors}\n"
    message += f"📖 {journal}\n"

    if keywords:
        message += f"🏷️ {', '.join(keywords)}\n"

    if abstract:
        short_abstract = abstract[:200] + "..." if len(abstract) > 200 else abstract
        message += f"\n_{short_abstract}_\n"

    if url:
        message += f"\n🔗 [閱讀全文]({url})"

    return send_to_channel(Channel.RESEARCH, message)


def send_research_idea(
    idea: str,
    category: Optional[str] = None,
    priority: Optional[str] = None
) -> bool:
    """發送研究 idea 到研究頻道

    Args:
        idea: 研究想法
        category: 分類（如：不動產、AI、財務）
        priority: 優先級（高/中/低）

    Returns:
        是否發送成功
    """
    timestamp = datetime.now().strftime('%Y-%m-%d %H:%M')

    priority_emoji = {"高": "🔴", "中": "🟡", "低": "🟢"}.get(priority, "💡")

    message = f"{priority_emoji} *研究 Idea*\n\n"
    message += f"📅 {timestamp}\n"

    if category:
        message += f"📂 {category}\n"

    message += f"\n{idea}\n"

    return send_to_channel(Channel.RESEARCH, message)


# ===== 投資相關通知（INVEST 頻道）=====

def send_stock_alert(
    stock_code: str,
    stock_name: str,
    alert_type: str,
    details: Optional[str] = None,
    price: Optional[float] = None,
    change: Optional[float] = None
) -> bool:
    """發送股票警示到投資頻道

    Args:
        stock_code: 股票代碼
        stock_name: 股票名稱
        alert_type: 警示類型（如：處置股、警示股、漲停、跌停）
        details: 詳細說明
        price: 當前價格
        change: 漲跌幅（%）

    Returns:
        是否發送成功
    """
    timestamp = datetime.now().strftime('%Y-%m-%d %H:%M')

    # 根據警示類型選擇 emoji
    type_emoji = {
        "處置股": "🚨",
        "警示股": "⚠️",
        "漲停": "🔺",
        "跌停": "🔻",
        "目標價": "🎯",
    }.get(alert_type, "📊")

    message = f"{type_emoji} *{alert_type}*\n\n"
    message += f"📅 {timestamp}\n"
    message += f"📈 {stock_code} {stock_name}\n"

    if price is not None:
        message += f"💰 ${price:.2f}"
        if change is not None:
            change_emoji = "📈" if change > 0 else "📉" if change < 0 else "➡️"
            message += f" {change_emoji} {change:+.2f}%"
        message += "\n"

    if details:
        message += f"\n{details}\n"

    return send_to_channel(Channel.INVEST, message)


def send_mops_notification(
    company_name: str,
    announcement_type: str,
    summary: str,
    amount: Optional[float] = None,
    appraiser: Optional[str] = None
) -> bool:
    """發送 MOPS 重大訊息到投資頻道

    Args:
        company_name: 公司名稱
        announcement_type: 公告類型
        summary: 摘要
        amount: 金額（億元）
        appraiser: 估價師/事務所

    Returns:
        是否發送成功
    """
    timestamp = datetime.now().strftime('%Y-%m-%d %H:%M')

    message = f"📢 *MOPS 重大訊息*\n\n"
    message += f"📅 {timestamp}\n"
    message += f"🏢 {company_name}\n"
    message += f"📋 {announcement_type}\n"

    if amount is not None:
        message += f"💰 {amount:.2f} 億元\n"

    if appraiser:
        message += f"👤 {appraiser}\n"

    message += f"\n{summary}\n"

    return send_to_channel(Channel.INVEST, message)


# ===== CLI 介面 =====
if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(
        description='Telegram 通知工具（多頻道版）',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog='''
頻道說明：
  work      工作相關（MOPS、估價師異動、客戶通知）
  daily     生活相關（天氣、記帳、餐廳、日誌、健康）
  dev       程式開發（Git Commit、部署通知）← 使用獨立 bot
  research  學術研究（論文監控、研究 ideas）
  invest    投資監控（股票警示、FinLab、公司重大訊息）
  blog      部落格教練（催稿貓週報）← 使用獨立 bot
  thesis    論文教練（畢業犬進度報告）← 使用獨立 bot

範例：
  python notifier.py "Hello"                      # 發送到預設頻道
  python notifier.py "今日天氣" --channel daily    # 發送到生活頻道
  python notifier.py "部署完成" --channel dev      # 發送到開發頻道
  python notifier.py "新論文" --channel research   # 發送到研究頻道
  python notifier.py "股票警示" --channel invest   # 發送到投資頻道
  python notifier.py "週報" --channel blog         # 發送到部落格頻道
  python notifier.py "進度" --channel thesis       # 發送到論文頻道
  python notifier.py --test                       # 測試所有頻道
  python notifier.py --status                     # 檢查頻道設定狀態
        '''
    )
    parser.add_argument('message', nargs='?', help='要發送的訊息')
    parser.add_argument('--type', choices=['success', 'error', 'info'], default=None,
                        help='訊息類型')
    parser.add_argument('--title', help='標題（搭配 --type 使用）')
    parser.add_argument('--channel', '-c', choices=['work', 'daily', 'dev', 'research', 'invest', 'blog', 'thesis'], default='work',
                        help='目標頻道（預設：work）')
    parser.add_argument('--test', action='store_true', help='發送測試訊息到所有頻道')
    parser.add_argument('--status', action='store_true', help='檢查頻道設定狀態')

    args = parser.parse_args()

    if args.status:
        print("\n頻道設定狀態：")
        print("-" * 40)
        for ch in Channel:
            chat_id = CHANNEL_CHAT_IDS.get(ch, '')
            status = "已設定" if chat_id else "未設定"
            masked_id = f"...{chat_id[-4:]}" if chat_id else "-"
            print(f"  {ch.value:<10} {status:<8} ({masked_id})")
        print()

    elif args.test:
        print("\n測試各頻道...")
        print("-" * 40)
        for ch in Channel:
            chat_id = CHANNEL_CHAT_IDS.get(ch, '')
            if chat_id:
                success = send_to_channel(ch, f"測試訊息\n\n頻道：{ch.value}\n時間：{datetime.now().strftime('%H:%M:%S')}")
                print(f"  {ch.value:<10} {'已發送' if success else '發送失敗'}")
            else:
                print(f"  {ch.value:<10} 未設定（跳過）")
        print()

    elif args.message:
        # 取得目標頻道
        channel_map = {
            'work': Channel.WORK,
            'daily': Channel.DAILY,
            'dev': Channel.DEV,
            'research': Channel.RESEARCH,
            'invest': Channel.INVEST,
            'blog': Channel.BLOG,
            'thesis': Channel.THESIS,
        }
        target_channel = channel_map[args.channel]

        if args.type == 'success':
            success = send_success(args.title or "成功", details={"訊息": args.message})
        elif args.type == 'error':
            success = send_error(args.title or "錯誤", error=args.message)
        elif args.type == 'info':
            success = send_info(args.title or "資訊", args.message)
        else:
            success = send_to_channel(target_channel, args.message)
        print(f"[{args.channel}] {'已發送' if success else '發送失敗'}")
    else:
        parser.print_help()
