from flask import Flask, request, jsonify
import re
import jieba
import json
import os
import logging
from datetime import datetime

app = Flask(__name__)

# --- 日志配置 ---
# 创建一个logger
logger = logging.getLogger('user_activity')
logger.setLevel(logging.INFO)

# 创建一个handler，用于写入日志文件
file_handler = logging.FileHandler('useruse.log', mode='a', encoding='utf-8')
file_handler.setLevel(logging.INFO)

# 创建一个handler，用于输出到控制台（可选）
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)

# 定义日志格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)

# 给logger添加handler
logger.addHandler(file_handler)
logger.addHandler(console_handler)

# --- 从文件加载自定义词汇 ---
JIEBA_DICT_FILE = 'jieba.txt'


def load_custom_words_from_file(file_path):
    """从指定文件加载自定义词汇"""
    custom_words = set()  # 使用set避免重复
    if not os.path.exists(file_path):
        print(f"警告: 找不到自定义词汇文件 '{file_path}'，将使用空列表。")
        return list(custom_words), set()

    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            for line in f:
                word = line.strip()  # 去除行首行尾的空白字符和换行符
                if word:  # 确保不是空行
                    custom_words.add(word)
    except Exception as e:
        print(f"错误: 读取自定义词汇文件 '{file_path}' 时发生异常: {e}")

    # 从加载的词汇中筛选出需要特殊处理的复合词（包含大写字母的驼峰命名）
    compound_words = {word for word in custom_words if any(c.isupper() for c in word)}
    simple_custom_words = custom_words - compound_words

    return list(simple_custom_words), compound_words


# 从文件加载自定义词汇
SIMPLE_CUSTOM_WORDS, COMPOUND_WORDS_SET = load_custom_words_from_file(JIEBA_DICT_FILE)

# 为简单词汇添加到jieba词典
for word in SIMPLE_CUSTOM_WORDS:
    jieba.add_word(word)
# print(f"成功从 '{JIEBA_DICT_FILE}' 加载了 {len(SIMPLE_CUSTOM_WORDS)} 个简单自定义词汇。")
# print(f"识别出 {len(COMPOUND_WORDS_SET)} 个复合词汇待处理: {list(COMPOUND_WORDS_SET)[:10]}...")  # 打印前10个作为示例

# 预编译复合词的正则表达式模式，以提高性能
# 例如: (MemoFlow|MookNote|VoceChat)
COMPOUND_PATTERN = re.compile('|'.join(re.escape(w) for w in COMPOUND_WORDS_SET), re.IGNORECASE)


def tokenize_text(text):
    """
    对中英文及数字进行分词
    - 复合词：优先匹配自定义的驼峰命名复合词
    - 中文：使用 jieba 分词
    - 英文：按空格和标点分词
    - 数字：单独提取
    """
    if not text or not text.strip():
        return []

    tokens = []
    # 定义正则表达式模式
    # 中文字符范围
    chinese_pattern = re.compile(r'[\u4e00-\u9fff]+')
    # 英文单词（包含连字符）
    english_pattern = re.compile(r'[a-zA-Z]+(?:-[a-zA-Z]+)*')
    # 数字（包含小数、负数）
    number_pattern = re.compile(r'-?\d+\.?\d*')

    # 当前处理位置
    pos = 0
    text_length = len(text)

    while pos < text_length:
        char = text[pos]

        # 跳过空白字符
        if char.isspace():
            pos += 1
            continue

        # --- 新增逻辑：优先匹配复合词 ---
        compound_match = COMPOUND_PATTERN.match(text, pos)
        if compound_match:
            matched_word = compound_match.group()
            tokens.append({
                'text': matched_word,
                'type': 'compound_eng',
                'length': len(matched_word)
            })
            pos += len(matched_word)
            continue  # 匹配到复合词后，跳过后续的其他匹配

        # 匹配中文字符串
        if chinese_pattern.match(char):
            # 找到连续的中文字符
            chinese_str = ''
            while pos < text_length and chinese_pattern.match(text[pos]):
                chinese_str += text[pos]
                pos += 1

            # 使用 jieba 对中文字符串进行分词
            # 注意：这里的 jieba.cut() 会使用我们之前 add_word 添加的自定义词汇
            chinese_tokens = list(jieba.cut(chinese_str))
            for token in chinese_tokens:
                if token.strip():
                    tokens.append({
                        'text': token,
                        'type': 'chinese',
                        'length': len(token)
                    })

        # 匹配英文单词
        elif english_pattern.match(char):
            match = english_pattern.match(text, pos)
            if match:
                word = match.group()
                tokens.append({
                    'text': word,
                    'type': 'english',
                    'length': len(word)
                })
                pos += len(word)

        # 匹配数字
        elif number_pattern.match(char) or (char == '-' and pos + 1 < text_length and text[pos + 1].isdigit()):
            match = number_pattern.match(text, pos)
            if match:
                number = match.group()
                tokens.append({
                    'text': number,
                    'type': 'number',
                    'length': len(number)
                })
                pos += len(number)

        # 其他字符（标点符号等），单独处理
        else:
            tokens.append({
                'text': char,
                'type': 'punctuation',
                'length': 1
            })
            pos += 1

    return tokens


def get_token_stats(tokens):
    """获取分词统计信息"""
    stats = {
        'total': len(tokens),
        'chinese': 0,
        'english': 0,
        'compound_eng': 0,  # 为复合词类型添加计数
        'number': 0,
        'punctuation': 0
    }

    for token in tokens:
        token_type = token['type']
        if token_type in stats:
            stats[token_type] += 1

    # 重新计算总数，确保准确性
    stats['total'] = sum(v for k, v in stats.items() if k != 'total')

    return stats


# 定义用户Token文件路径
TOKEN_FILE_PATH = 'usertoken.json'


# 启动时加载Token文件
def load_valid_tokens():
    """从文件加载有效的Token列表"""
    if not os.path.exists(TOKEN_FILE_PATH):
        print(f"错误: 找不到Token文件 '{TOKEN_FILE_PATH}'")
        return {}

    try:
        with open(TOKEN_FILE_PATH, 'r', encoding='utf-8') as f:
            tokens_data = json.load(f)

        # 将列表转换为以token为键的字典，便于快速查找
        token_map = {}
        for item in tokens_data:
            # 确保数据格式正确
            if 'user' in item and 'token' in item:
                token_map[item['token']] = item['user']
            else:
                print(f"警告: Token文件中发现格式错误的条目: {item}")

        # print(f"成功从 '{TOKEN_FILE_PATH}' 加载了 {len(token_map)} 个有效Token。")
        return token_map

    except json.JSONDecodeError as e:
        print(f"错误: Token文件 '{TOKEN_FILE_PATH}' 不是有效的JSON格式。{e}")
        return {}
    except Exception as e:
        print(f"错误: 读取Token文件时发生异常: {e}")
        return {}


# 程序启动时加载一次
VALID_TOKENS_MAP = load_valid_tokens()


def get_user_by_token(token):
    """根据token获取用户名，如果无效则返回None"""
    return VALID_TOKENS_MAP.get(token)


def authenticate_request():
    """检查请求头中的Authorization token，并返回用户信息"""
    auth_header = request.headers.get('Authorization')
    if not auth_header or not auth_header.startswith('Bearer '):
        return None, "缺少或格式错误的Authorization头。请使用 'Authorization: Bearer <your_token>'"

    token = auth_header.split(" ", 1)[1]
    user = get_user_by_token(token)

    if not user:
        return None, "无效的Token"

    return user, None


@app.route('/tokenize', methods=['POST'])
def tokenize_endpoint():
    """处理分词请求的 API 端点"""
    user, error_msg = authenticate_request()
    if not user:
        return jsonify({'success': False, 'error': error_msg}), 401

    data = request.get_json()
    input_text = data.get('input_text', '')

    # 记录日志：谁在什么时候输入了什么内容
    log_message = f"User '{user}' submitted text: '{input_text}'"
    logger.info(log_message)
    print(f"Log Recorded: {log_message}")  # 控制台也打印一下，方便即时查看

    try:
        # 执行分词
        tokens = tokenize_text(input_text)

        # 获取统计信息
        stats = get_token_stats(tokens)

        # 返回成功的 JSON 响应，包含请求的用户信息
        return jsonify({
            'success': True,
            'tokens': tokens,
            'stats': stats,
            'original_text': input_text,
            'requested_by': user  # 添加请求者信息
        })

    except Exception as e:
        # 如果处理出错，也记录一条错误日志
        error_log_message = f"Error processing request for user '{user}': {str(e)}"
        logger.error(error_log_message)

        return jsonify({
            'success': False,
            'error': f'分词处理失败: {str(e)}'
        }), 500


# --- 以下为原有的分词和统计逻辑，保持不变 ---
# (tokenize_text 和 get_token_stats 已经在上面定义过了)


if __name__ == '__main__':
    app.run(debug=False, host='0.0.0.0', port=27056)  # 生产环境建议关闭debug