#!/usr/bin/env python3
"""
事件聚类模块

功能：
1. 基于时间窗口和主题相似度聚类推文
2. 识别独立事件
3. 构建事件-推文映射关系
"""

import json
from datetime import datetime, timedelta
from typing import List, Dict, Any, Optional
from collections import defaultdict
import re


def parse_tweet_time(time_str: str) -> Optional[datetime]:
    """解析推文时间戳"""
    formats = [
        "%Y-%m-%dT%H:%M:%SZ",
        "%Y-%m-%dT%H:%M:%S.%fZ",
        "%Y-%m.-%d %H:%M:%S",
        "%Y-%m-%d %H:%M:%S",
    ]

    for fmt in formats:
        try:
            return datetime.strptime(time_str, fmt)
        except (ValueError, TypeError):
            continue

    return None


def extract_hashtags(text: str) -> List[str]:
    """提取话题标签"""
    hashtags = re.findall(r'#(\w+)', text)
    return [h.lower() for h in hashtags]


def extract_mentions(text: str) -> List[str]:
    """提取@提及"""
    mentions = re.findall(r'@(\w+)', text)
    return list(set(mentions))


def calculate_text_similarity(text1: str, text2: str) -> float:
    """
    计算文本相似度（简化版Jaccard相似度）

    Returns:
        float: 0-1之间的相似度分数
    """
    # 提取关键词（简单分词，实际应用中应使用NLP）
    words1 = set(re.findall(r'\w+', text1.lower()))
    words2 = set(re.findall(r'\w+', text2.lower()))

    if not words1 or not words2:
        return 0.0

    intersection = len(words1.intersection(words2))
    union = len(words1.union(words2))

    return intersection / union if union > 0 else 0.0


def calculate_hashtag_overlap(hashtags1: List[str], hashtags2: List[str]) -> float:
    """计算话题标签重叠度"""
    if not hashtags1 or not hashtags2:
        return 0.0

    set1 = set(hashtags1)
    set2 = set(hashtags2)
    intersection = len(set1.intersection(set2))

    return intersection / min(len(set1), len(set2))


def should_belong_to_same_event(
    tweet1: Dict[str, Any],
    tweet2: Dict[str, Any],
    time_window_hours: float = 48.0,
    similarity_threshold: float = 0.3
) -> bool:
    """
    判断两条推文是否属于同一事件

    Args:
        tweet1, tweet2: 推文数据
        time_window_hours: 时间窗口（小时）
        similarity_threshold: 相似度阈值

    Returns:
        bool: 是否属于同一事件
    """
    # 1. 时间窗口检查
    time1 = tweet1.get("parsed_time")
    time2 = tweet2.get("parsed_time")

    if not time1 or not time2:
        return False

    time_diff = abs((time1 - time2).total_seconds()) / 3600.0
    if time_diff > time_window_hours:
        return False

    # 2. 话题标签重叠检查（重要信号）
    hashtags1 = extract_hashtags(tweet1.get("text", ""))
    hashtags2 = extract_hashtags(tweet2.get("text", ""))

    if hashtags1 and hashtags2:
        hashtag_overlap = calculate_hashtag_overlap(hashtags1, hashtags2)
        if hashtag_overlap >= 0.5:  # 高重叠度
            return True

    # 3. 文本相似度检查
    text1 = tweet1.get("text", "")
    text2 = tweet2.get("text", "")

    text_similarity = calculate_text_similarity(text1, text2)
    if text_similarity >= similarity_threshold:
        return True

    # 4. 转推/引用关系检查
    if tweet1.get("is_retweet_of") == tweet2.get("id"):
        return True
    if tweet2.get("is_retweet_of") == tweet1.get("id"):
        return True

    return False


def cluster_tweets_into_events(
    tweets: List[Dict[str, Any]],
    time_window_hours: float = 48.0,
    min_tweets_per_event: int = 3
) -> List[Dict[str, Any]]:
    """
    将推文聚类为事件

    Args:
        tweets: 推文列表
        time_window_hours: 时间窗口（小时）
        min_tweets_per_event: 每个事件最少的推文数量

    Returns:
        List[Dict]: 事件列表
    """
    if not tweets:
        return []

    # 预处理：解析时间戳
    for tweet in tweets:
        tweet["parsed_time"] = parse_tweet_time(tweet.get("created_at", ""))
        tweet["hashtags"] = extract_hashtags(tweet.get("text", ""))
        tweet["mentions"] = extract_mentions(tweet.get("text", ""))

    # 按时间排序
    sorted_tweets = sorted([t for t in tweets if t["parsed_time"]], key=lambda x: x["parsed_time"])

    # 聚类算法：时间窗口 + 相似度
    events = []
    current_event = {
        "id": f"event_{len(events)}",
        "tweets": [sorted_tweets[0]],
        "start_time": sorted_tweets[0]["parsed_time"],
        "end_time": sorted_tweets[0]["parsed_time"],
    }

    for tweet in sorted_tweets[1:]:
        # 检查是否属于当前事件
        belongs_to_current = any(
            should_belong_to_same_event(tweet, t, time_window_hours)
            for t in current_event["tweets"][-5:]  # 只检查最近5条推文，优化性能
        )

        if belongs_to_current:
            # 加入当前事件
            current_event["tweets"].append(tweet)
            current_event["end_time"] = tweet["parsed_time"]
        else:
            # 保存当前事件（如果满足最小推文数）
            if len(current_event["tweets"]) >= min_tweets_per_event:
                events.append(current_event)

            # 创建新事件
            current_event = {
                "id": f"event_{len(events)}",
                "tweets": [tweet],
                "start_time": tweet["parsed_time"],
                "end_time": tweet["parsed_time"],
            }

    # 保存最后一个事件
    if len(current_event["tweets"]) >= min_tweets_per_event:
        events.append(current_event)

    # 提取事件特征
    for event in events:
        extract_event_features(event)

    return events


def extract_event_features(event: Dict[str, Any]):
    """提取事件特征"""
    tweets = event["tweets"]

    # 时间特征
    event["duration_hours"] = (event["end_time"] - event["start_time"]).total_seconds() / 3600.0
    event["start_time_str"] = event["start_time"].strftime("%Y-%m-%d %H:%M:%S")
    event["end_time_str"] = event["end_time"].strftime("%Y-%m-%d %H:%M:%S")

    # 统计特征
    event["tweet_count"] = len(tweets)
    event["original_count"] = sum(1 for t in tweets if not t.get("is_retweet_of"))
    event["retweet_count"] = sum(1 for t in tweets if t.get("is_retweet_of"))

    # 互动统计
    total_likes = sum(t.get("like_count", 0) for t in tweets)
    total_retweets = sum(t.get("retweet_count", 0) for t in tweets)
    total_replies = sum(t.get("reply_count", 0) for t in tweets)

    event["total_engagement"] = {
        "likes": total_likes,
        "retweets": total_retweets,
        "replies": total_replies,
        "total": total_likes + total_retweets + total_replies,
    }

    # 话题标签
    all_hashtags = []
    for t in tweets:
        all_hashtags.extend(t.get("hashtags", []))

    hashtag_counts = defaultdict(int)
    for h in all_hashtags:
        hashtag_counts[h] += 1

    event["hashtags"] = [
        {"tag": tag, "count": count}
        for tag, count in sorted(hashtag_counts.items(), key=lambda x: x[1], reverse=True)
    ]

    # 提及
    all_mentions = []
    for t in tweets:
        all_mentions.extend(t.get("mentions", []))

    mention_counts = defaultdict(int)
    for m in all_mentions:
        mention_counts[m] += 1

    event["mentions"] = [
        {"account": mention, "count": count}
        for mention, count in sorted(mention_counts.items(), key=lambda x: x[1], reverse=True)[:10]
    ]

    # 关键推文（互动量最高的原创推文）
    original_tweets = [t for t in tweets if not t.get("is_retweet_of")]
    if original_tweets:
        key_tweet = max(original_tweets, key=lambda t: t.get("like_count", 0) + t.get("retweet_count", 0))
        event["key_tweet"] = {
            "id": key_tweet.get("id"),
            "text": key_tweet.get("text", "")[:200] + "..." if len(key_tweet.get("text", "")) > 200 else key_tweet.get("text", ""),
            "created_at": key_tweet.get("created_at"),
            "like_count": key_tweet.get("like_count", 0),
            "retweet_count": key_tweet.get("retweet_count", 0),
            "reply_count": key_tweet.get("reply_count", 0),
        }
    else:
        event["key_tweet"] = None


def main():
    """命令行入口，用于测试"""
    import sys

    # 示例数据
    example_tweets = [
        {
            "id": "1",
            "text": "Exciting announcement coming soon! #launch",
            "created_at": "2024-03-15T10:00:00Z",
            "like_count": 1000,
            "retweet_count": 500,
            "reply_count": 100,
        },
        {
            "id": "2",
            "text": "The launch is happening tomorrow #launch #product",
            "created_at": "2024-03-15T11:00:00Z",
            "like_count": 2000,
            "retweet_count": 800,
            "reply_count": 200,
        },
        {
            "id": "3",
            "text": "Here are more details about the launch #launch",
            "created_at": "2024-03-15T12:00:00Z",
            "like_count": 1500,
            "retweet_count": 600,
            "reply_count": 150,
        },
    ]

    if len(sys.argv) > 1:
        # 从文件读取
        with open(sys.argv[1], 'r', encoding='utf-8') as f:
            example_tweets = json.load(f)

    events = cluster_tweets_into_events(example_tweets)
    print(json.dumps(events, ensure_ascii=False, indent=2, default=str))


if __name__ == "__main__":
    main()
