import traceback
import uuid
import time
import json
import secrets
from datetime import datetime

import loguru
from flask import request, jsonify, Flask,Blueprint

from aidso_geo.config.base_config import init_redis8, PlatformType
from aidso_geo.utils import bh_utils, tos_utils


SYSTEM_ALLOW_PLATFORM ={i.value for i in PlatformType}
third_app = Blueprint("third_app", __name__)

redis_client = init_redis8()
def ok(data=None, msg="ok"):
    return jsonify({"code": 200, "data": data, "msg": msg}), 200

def parse_platform_costs(raw_value):
    """
    解析平台扣费配置

    支持：
    1. dict: {"DB": 10, "DP": 20}
    2. json string: '{"DB": 10, "DP": 20}'
    """
    if raw_value is None:
        return {}

    if isinstance(raw_value, dict):
        result = {}
        for k, v in raw_value.items():
            platform = str(k).strip()
            if not platform:
                continue
            try:
                cost = int(v)
            except Exception:
                continue
            if cost > 0:
                result[platform] = cost
        return result

    if isinstance(raw_value, str):
        raw_value = raw_value.strip()
        if not raw_value:
            return {}

        try:
            obj = json.loads(raw_value)
        except Exception:
            return {}

        if not isinstance(obj, dict):
            return {}

        result = {}
        for k, v in obj.items():
            platform = str(k).strip()
            if not platform:
                continue
            try:
                cost = int(v)
            except Exception:
                continue
            if cost > 0:
                result[platform] = cost
        return result

    return {}


def calc_platform_cost(normalized_platforms, platform_costs, thinking_costs=None):

    thinking_costs = thinking_costs or {}

    total_cost = 0
    cost_detail = {}

    for item in normalized_platforms:
        name = item["name"]

        if name not in platform_costs:
            raise ValueError(f"平台 {name} 未配置扣费积分")

        base_cost = int(platform_costs[name])
        if base_cost <= 0:
            raise ValueError(f"平台 {name} 扣费积分必须大于 0")

        thinking_extra_cost = 0
        if str(item.get("thinkingEnabled", "0")) == "1":
            thinking_extra_cost = int(thinking_costs.get(name, 0))

        cost = base_cost + thinking_extra_cost

        total_cost += cost
        cost_detail[name] = {
            "base_cost": base_cost,
            "thinking_extra_cost": thinking_extra_cost,
            "total_cost": cost
        }

    return total_cost, cost_detail

def get_history_used_list(channel: str):
    history_userd_list = []
    daily_map = redis_client.hgetall("third_geo_daily") or {}

    prefix = f"{channel}"
    prefix_len = len(prefix)

    for key, value in daily_map.items():
        if not key.startswith(prefix):
            continue

        day_str = key[prefix_len:]
        if len(day_str) != 8 or not day_str.isdigit():
            continue

        history_userd_list.append({
            "date": day_str,
            "used": int(value) if value else 0
        })

    history_userd_list.sort(key=lambda x: x["date"], reverse=True)
    return history_userd_list

def parse_thinking_costs(raw_value):
    """
    解析 thinkingEnabled=1 时的平台叠加扣费配置

    支持：
    1. dict: {"DB": 5, "DP": 8}
    2. json string: '{"DB": 5, "DP": 8}'

    历史兼容：
    1. 没有 thinking_costs 字段
    2. thinking_costs 为空
    3. 某个平台没有配置
    都默认不叠加积分
    """
    if raw_value is None:
        return {}

    if isinstance(raw_value, dict):
        result = {}
        for k, v in raw_value.items():
            platform = str(k).strip()
            if not platform:
                continue
            try:
                cost = int(v)
            except Exception:
                continue
            if cost > 0:
                result[platform] = cost
        return result

    if isinstance(raw_value, str):
        raw_value = raw_value.strip()
        if not raw_value:
            return {}

        try:
            obj = json.loads(raw_value)
        except Exception:
            return {}

        if not isinstance(obj, dict):
            return {}

        result = {}
        for k, v in obj.items():
            platform = str(k).strip()
            if not platform:
                continue
            try:
                cost = int(v)
            except Exception:
                continue
            if cost > 0:
                result[platform] = cost
        return result

    return {}

def err(code, msg, http_status=400):
    return jsonify({"code": code, "data": None, "msg": msg}), http_status


def gen_authorization():
    return secrets.token_hex(16).upper()

type_map ={
    '0':"stream_batch",
    '1':"batch"
}


def parse_allow_platforms(raw_value):
    if raw_value is None:
        return set()

    if isinstance(raw_value, list):
        return {str(x).strip() for x in raw_value if str(x).strip()}

    if isinstance(raw_value, str):
        raw_value = raw_value.strip()
        if not raw_value:
            return set()
        try:
            arr = json.loads(raw_value)
            if not isinstance(arr, list):
                return set()
            return {str(x).strip() for x in arr if str(x).strip()}
        except Exception as e:
            traceback.print_exc()
            return set()

    return set()


def get_auth_info_by_token(auth_token: str):
    if not auth_token:
        return None

    sql = """
        select channel, authorization, allow_platforms, status, daily_limit, total_limit, platform_costs, thinking_costs
        from geo_third_token
        where authorization = %s
        limit 1
    """

    data = bh_utils.query_data(sql, (auth_token,))
    if not data:
        return None
    return data[0]


def get_today_limit_info(channel: str, daily_limit: int):
    today_pt = datetime.now().strftime("%Y%m%d")
    redis_limit_key = f"{channel}{today_pt}"
    used_count = redis_client.hget("third_geo_daily", redis_limit_key)
    used_count = int(used_count) if used_count else 0
    remain_count = max(daily_limit - used_count, 0)
    return redis_limit_key, used_count, remain_count


def get_total_limit_info(channel: str, total_limit: int):
    redis_total_key = channel
    used_count = redis_client.hget("third_geo_total", redis_total_key)
    used_count = int(used_count) if used_count else 0
    remain_count = max(total_limit - used_count, 0)
    return redis_total_key, used_count, remain_count


@third_app.route('/open/auth/create', methods=['POST'])
def create_auth():
    data = request.get_json(silent=True)
    if not isinstance(data, dict):
        return err(400, "invalid json body (must be an object)")

    channel = str(data.get("channel", "")).strip()
    daily_limit = data.get("daily_limit", 50)
    total_limit = data.get("total_limit")
    remark = str(data.get("remark", "")).strip()
    allow_platforms = data.get("allow_platforms")
    platform_costs = data.get("platform_costs")
    thinking_costs = data.get("thinking_costs")

    if not channel:
        return err(400, "missing required field: channel")

    if allow_platforms in (None, "", [], {}):
        return err(400, "missing required field: allow_platforms")

    if not isinstance(allow_platforms, list) or len(allow_platforms) == 0:
        return err(400, "allow_platforms must be a non-empty list")

    normalized_platforms = []
    seen = set()
    for idx, p in enumerate(allow_platforms):
        if not isinstance(p, str):
            return err(400, f"allow_platforms[{idx}] must be string")

        p = p.strip()
        if not p:
            return err(400, f"allow_platforms[{idx}] cannot be empty")

        if p not in SYSTEM_ALLOW_PLATFORM:
            return err(400, f"allow_platforms[{idx}] '{p}' not allowed")

        # if p in seen:
        #     return err(400, f"duplicate allow_platforms value: {p}")

        seen.add(p)
        normalized_platforms.append(p)
    normalized_platform_costs = {}
    normalized_thinking_costs = {}

    # platform_costs 不传，表示保持老逻辑：一个平台扣 1
    # platform_costs 传了，就必须是 object，并且 allow_platforms 里的每个平台都要配置积分
    if platform_costs not in (None, "", [], {}):
        if not isinstance(platform_costs, dict):
            return err(400, "platform_costs must be an object")

        for p in normalized_platforms:
            if p not in platform_costs:
                return err(400, f"platform_costs missing platform: {p}")

            try:
                cost = int(platform_costs[p])
            except Exception:
                return err(400, f"platform_costs.{p} must be int")

            if cost <= 0:
                return err(400, f"platform_costs.{p} must be > 0")

            normalized_platform_costs[p] = cost
    if thinking_costs not in (None, "", [], {}):
        if not isinstance(thinking_costs, dict):
            return err(400, "thinking_costs must be an object")

        for p, v in thinking_costs.items():
            p = str(p).strip()

            if not p:
                return err(400, "thinking_costs platform cannot be empty")

            if p not in normalized_platforms:
                return err(400, f"thinking_costs platform '{p}' not in allow_platforms")

            try:
                cost = int(v)
            except Exception:
                return err(400, f"thinking_costs.{p} must be int")

            if cost < 0:
                return err(400, f"thinking_costs.{p} must be >= 0")

            if cost > 0:
                normalized_thinking_costs[p] = cost

    try:
        daily_limit = int(daily_limit)
    except Exception:
        return err(400, "daily_limit must be int")

    if daily_limit <= 0:
        return err(400, "daily_limit must be > 0")

    if total_limit is None:
        return err(400, "missing required field: total_limit")

    try:
        total_limit = int(total_limit)
    except Exception:
        return err(400, "total_limit must be int")

    if total_limit <= 0:
        return err(400, "total_limit must be > 0")

    if total_limit < daily_limit:
        return err(400, "total_limit must be >= daily_limit")

    exists = bh_utils.query_data(
        """
        select channel, authorization, daily_limit, total_limit, allow_platforms, platform_costs, thinking_costs
        from geo_third_token
        where channel = %s
          and status = 1
        limit 1
        """,
        (channel,)
    )
    if exists:
        _, daily_used_count, daily_remain_count = get_today_limit_info(
            exists[0]["channel"],
            int(exists[0]["daily_limit"])
        )
        _, total_used_count, total_remain_count = get_total_limit_info(
            exists[0]["channel"],
            int(exists[0]["total_limit"])
        )
        return ok({
            "channel": exists[0]["channel"],
            "authorization": exists[0]["authorization"],
            "daily_limit": int(exists[0]["daily_limit"]),
            "daily_used_count": daily_used_count,
            "daily_remain_count": daily_remain_count,
            "total_limit": int(exists[0]["total_limit"]),
            "total_used_count": total_used_count,
            "total_remain_count": total_remain_count,
            "allow_platforms": json.loads(exists[0]["allow_platforms"]) if exists[0].get("allow_platforms") else [],
            "platform_costs": json.loads(exists[0]["platform_costs"]) if exists[0].get("platform_costs") else {},
            "thinking_costs": json.loads(exists[0]["thinking_costs"]) if exists[0].get("thinking_costs") else {}
        }, msg="channel already exists")

    authorization = gen_authorization()
    now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    insert_data = [{
        "channel": channel,
        "authorization": authorization,
        "allow_platforms": json.dumps(normalized_platforms, ensure_ascii=False),
        "status": 1,
        "daily_limit": daily_limit,
        "total_limit": total_limit,
        "remark": remark,
        "create_time": now_str,
        "update_time": now_str,
        "platform_costs": json.dumps(normalized_platform_costs, ensure_ascii=False),
        "thinking_costs": json.dumps(normalized_thinking_costs, ensure_ascii=False)

    }]

    if bh_utils.insert_data("geo_third_token", insert_data):
        return ok({
            "channel": channel,
            "authorization": authorization,
            "allow_platforms": normalized_platforms,
            "daily_limit": daily_limit,
            "daily_used_count": 0,
            "daily_remain_count": daily_limit,
            "total_limit": total_limit,
            "total_used_count": 0,
            "total_remain_count": total_limit,
            "platform_costs": normalized_platform_costs,
            "thinking_costs": normalized_thinking_costs
        })
    else:
        return err(500, "生成Authorization失败", 500)


@third_app.route('/open/mt/task_commit', methods=['POST'])
def mt_task_commit():
    auth_header = request.headers.get('Authorization')
    if not auth_header:
        return err(401, "缺少Authorization请求头", 401)

    auth_info = get_auth_info_by_token(auth_header)
    if not auth_info:
        return err(401, "Authorization参数错误", 401)

    if int(auth_info.get("status", 0)) != 1:
        return err(401, "Authorization已禁用", 401)

    channel = auth_info["channel"]
    daily_limit = int(auth_info.get("daily_limit", 50))
    total_limit = int(auth_info.get("total_limit", 0))
    channel_allow_platforms = parse_allow_platforms(auth_info.get("allow_platforms"))

    if not channel_allow_platforms:
        return err(401, "当前Authorization未配置允许平台", 401)

    data = request.get_json(silent=True)
    if not isinstance(data, dict):
        return err(400, "invalid json body (must be an object)")

    prompt = data.get("prompt")
    plat_form = data.get("platform")
    if len(prompt) > 256:
        return err(400, "prompt长度不能超过256个字符")
    if not prompt:
        return err(400, "missing required field: prompt")

    if plat_form in (None, "", [], {}):
        return err(400, "missing required field: platform")

    if not isinstance(plat_form, list) or len(plat_form) == 0:
        return err(400, "platform must be a non-empty list")

    normalized = []
    seen = set()

    for idx, item in enumerate(plat_form):
        if not isinstance(item, dict):
            return err(400, f"platform[{idx}] must be an object")

        name = item.get("name")
        if not name:
            return err(400, f"platform[{idx}].name is required")

        if name not in SYSTEM_ALLOW_PLATFORM:
            return err(400, f"platform[{idx}].name '{name}' not allowed")

        if name not in channel_allow_platforms:
            return err(400, f"platform[{idx}].name '{name}' is not allowed for current Authorization")

        if name in seen:
            return err(400, f"duplicate platform name: {name}")
        seen.add(name)

        thinking_enabled = str(item.get("thinkingEnabled", "0"))
        if thinking_enabled not in {"0", "1"}:
            return err(400, f"platform[{idx}].thinkingEnabled must be '0' or '1'")

        type = str(item.get("type", "1"))
        if type not in {"0", "1"}:
            return err(400, f"platform[{idx}].type must be '0' or '1'")

        is_share = str(item.get("is_share", "0"))
        if is_share not in {"0", "1"}:
            return err(
                400,
                f"platform[{idx}].is_share must be '0' or '1'"
            )

        normalized.append({
            "name": name,
            "thinkingEnabled": thinking_enabled,
            "type": type,
            "is_share": is_share
        })

    share_requested_platforms = {
        item["name"]
        for item in normalized
        if item["is_share"] == "1"
    }
    if share_requested_platforms:
        try:
            share_platforms_raw = tos_utils.get_string_from_tos(
                "geo_config/share_platforms.json"
            )
            share_platforms = json.loads(share_platforms_raw)
        except Exception:
            traceback.print_exc()
            return err(500, "分享平台配置读取失败", 500)

        if not isinstance(share_platforms, list):
            return err(
                500,
                "分享平台配置格式错误，必须是平台数组",
                500
            )

        supported_share_platforms = {
            str(platform).strip()
            for platform in share_platforms
            if str(platform).strip()
        }
        unsupported_share_platforms = sorted(
            share_requested_platforms - supported_share_platforms
        )
        if unsupported_share_platforms:
            return err(
                400,
                "平台不支持分享: "
                + ", ".join(unsupported_share_platforms)
            )

    platform_costs = parse_platform_costs(auth_info.get("platform_costs"))
    thinking_costs = parse_thinking_costs(auth_info.get("thinking_costs"))

    if platform_costs:
        try:
            cost, cost_detail = calc_platform_cost(normalized, platform_costs, thinking_costs)
        except ValueError as e:
            return err(400, str(e))
    else:
        cost = 0
        cost_detail = {}

        for item in normalized:
            name = item["name"]
            base_cost = 1
            thinking_extra_cost = 0

            if str(item.get("thinkingEnabled", "0")) == "1":
                thinking_extra_cost = int(thinking_costs.get(name, 0))

            item_cost = base_cost + thinking_extra_cost
            cost += item_cost

    daily_redis_limit_key, daily_used_count, _ = get_today_limit_info(channel, daily_limit)
    if daily_used_count + cost > daily_limit:
        return err(
            400,
            f"超过单日最大调用 {daily_limit}分/天，"
            f"本次提交消耗 {cost}，当前已使用 {daily_used_count}，剩余可用 {max(daily_limit - daily_used_count, 0)}"
        )

    total_redis_limit_key, total_used_count, _ = get_total_limit_info(channel, total_limit)
    if total_used_count + cost > total_limit:
        return err(
            400,
            f"超过渠道总最大调用 {total_limit}分，"
            f"本次提交消耗 {cost}，当前已使用 {total_used_count}，剩余可用 {max(total_limit - total_used_count, 0)}"
        )

    task_id = str(uuid.uuid4())
    req_ids = {}
    request_list = []
    now_ts = int(time.time())

    for p in normalized:
        req_id = str(uuid.uuid4())
        name = p["name"]

        request_list.append({
            "prompt": prompt,
            "taskId": task_id,
            "reqId": req_id,
            "platform": name,
            "thinkingEnabled": p["thinkingEnabled"],
            "type": type_map[p["type"]],
            "is_share": p["is_share"],
            "insertime": now_ts,
            "status": "ING",
            "channel": channel
        })
        req_ids[name] = req_id

    if bh_utils.insert_data("geo_third_task_data", request_list):
        current_daily_count = redis_client.hincrby("third_geo_daily", daily_redis_limit_key, cost)
        current_total_count = redis_client.hincrby("third_geo_total", total_redis_limit_key, cost)

        return ok({
            "taskId": task_id,
            "reqIds": req_ids,
        })
    else:
        return err(500, "服务器繁忙请重试", 500)


@third_app.route('/open/mt/get_result', methods=['GET'])
def mt_get_result():
    auth_header = request.headers.get('Authorization')
    if not auth_header:
        return err(401, "缺少Authorization请求头", 401)

    auth_info = get_auth_info_by_token(auth_header)
    if not auth_info:
        return err(401, "Authorization参数错误", 401)

    if int(auth_info.get("status", 0)) != 1:
        return err(401, "Authorization已禁用", 401)

    channel = auth_info["channel"]
    daily_limit = int(auth_info.get("daily_limit", 50))
    total_limit = int(auth_info.get("total_limit", 0))

    _, daily_used_count, daily_remain_count = get_today_limit_info(channel, daily_limit)
    _, total_used_count, total_remain_count = get_total_limit_info(channel, total_limit)

    req_id = request.args.get('reqId')
    if not req_id:
        return err(400, "missing required param: reqId")

    try:
        data = bh_utils.query_data(
            """
            select *
            from geo_third_task_data
            where reqId = %s
              and channel = %s
            limit 1
            """,
            (req_id, channel)
        )
    except Exception as e:
        return jsonify({
            "code": 500,
            "data": {},
            "msg": f"查询失败：{str(e)}"
        }), 500

    if not data:
        return jsonify({
            "code": 400,
            "msg": "reqId 未提交",
            "data": {
                "total_limit": total_limit,
                "total_used_count": total_used_count,
                "total_remain_count": total_remain_count
            }
        }), 400

    task_info = data[0]
    status = task_info['status']
    prompt = task_info['prompt']
    task_id = task_info['taskId']
    platform = task_info['platform']
    fetch_time = task_info['insertime']

    if status in ('ING', 'PROCESSING'):
        return jsonify({
            "code": 200,
            "msg": "success",
            "data": {
                "prompt": prompt,
                "status": "ING",
                "result": [],
                "fetch_time": fetch_time
            }
        }), 200

    if status == 'SUCCESS':
        file_list = {
            "search_word": "search_keyword.txt",
            "quote": "quote.txt",
            "think": "think.txt",
            "context": "context.txt",
            "suggestions": "suggestions.txt",
            "rich_media_block": "rich_media_block.txt",
            "share_url": "share_url.text"
        }

        result = []
        for k, v in file_list.items():
            content = tos_utils.get_string_from_tos(f"geo/{task_id}/{platform}/{v}")
            result.append({f"{k}":content if content else ""})

        return jsonify({
            "code": 200,
            "msg": "success",
            "data": {
                "status": "SUCCESS",
                "prompt": prompt,
                "result": result,
                "fetch_time": fetch_time
            }
        }), 200

@third_app.route('/open/mt/search_prompt', methods=['POST'])
def mt_search_commit():
    auth_header = request.headers.get('Authorization')
    if not auth_header:
        return err(401, "缺少Authorization请求头", 401)

    auth_info = get_auth_info_by_token(auth_header)
    if not auth_info:
        return err(401, "Authorization参数错误", 401)

    if int(auth_info.get("status", 0)) != 1:
        return err(401, "Authorization已禁用", 401)

    channel = auth_info["channel"]

    data = request.get_json(silent=True)
    if not isinstance(data, dict):
        return err(400, "invalid json body (must be an object)")

    keyword = str(data.get("keyword", "")).strip()
    if not keyword:
        return err(400, "missing required field: keyword")

    limit = data.get("limit", 100)
    try:
        limit = int(limit)
    except Exception:
        return err(400, "limit must be int")

    if limit <= 0:
        return err(400, "limit must be > 0")

    limit = min(limit, 100)

    try:
        query_sql = """
    SELECT
        prompt,
        reqId,
        taskId,
        platform,
        status,
        insertime
    FROM geo_third_task_data
    WHERE channel = %(channel)s
      AND prompt LIKE %(keyword)s
    ORDER BY insertime DESC
    LIMIT %(limit)s
            """

        rows = bh_utils.query_data(query_sql, {
            "channel": channel,
            "keyword": f"%{keyword}%",
            "limit": limit
        }) or []

    except Exception as e:
        return jsonify({
            "code": 500,
            "data": None,
            "msg": f"查询失败：{str(e)}"
        }), 500

    result_map = {}

    for row in rows:
        prompt = row.get("prompt", "")
        if not prompt:
            continue

        if prompt not in result_map:
            result_map[prompt] = {
                "prompt": prompt,
                "reqList": []
            }

        result_map[prompt]["reqList"].append({
            "reqId": row.get("reqId", ""),
            "taskId": row.get("taskId", ""),
            "platform": row.get("platform", ""),
            "status": row.get("status", ""),
            "insertTime": row.get("insertime", 0)
        })

    return ok({
        "keyword": keyword,
        "list": list(result_map.values())
    })

@third_app.route('/open/mt/get_usage', methods=['GET'])
def mt_get_usage():
    auth_header = request.headers.get('Authorization')
    if not auth_header:
        return err(401, "缺少Authorization请求头", 401)

    auth_info = get_auth_info_by_token(auth_header)
    if not auth_info:
        return err(401, "Authorization参数错误", 401)

    if int(auth_info.get("status", 0)) != 1:
        return err(401, "Authorization已禁用", 401)

    channel = auth_info["channel"]
    daily_limit = int(auth_info.get("daily_limit", 50))
    total_limit = int(auth_info.get("total_limit", 0))

    _, today_used, _ = get_today_limit_info(channel, daily_limit)
    _, total_used, total_remain = get_total_limit_info(channel, total_limit)
    history_userd_list = get_history_used_list(channel)

    return ok({
        "today_used": today_used,
        "total_used": total_used,
        "total_remain":total_remain

        # "history_used_list": history_userd_list
    })

