from datetime import datetime
from hmac import compare_digest

from flask import (
    jsonify,
    Blueprint,
    redirect,
    render_template,
    request,
    session,
    url_for,
)
from aidso_geo.config.base_config import init_redis
from aidso_geo.utils import bh_utils

dashboard_app = Blueprint("dashboard", __name__)

redis_client = init_redis()

DASHBOARD_USERNAME = "aidso"
DASHBOARD_PASSWORD = "aidso666"
DASHBOARD_SESSION_KEY = "aidso_dashboard_authenticated"


@dashboard_app.before_request
def require_dashboard_login():
    if request.endpoint in {
        "dashboard.dashboard_login",
        "dashboard.dashboard_logout",
    }:
        return None

    if session.get(DASHBOARD_SESSION_KEY):
        return None

    if request.path.startswith("/api/queue/"):
        return jsonify({
            "code": 401,
            "msg": "login required",
            "data": {},
        }), 401

    return redirect(url_for("dashboard.dashboard_login"))


@dashboard_app.route("/queue/login", methods=["GET", "POST"])
def dashboard_login():
    if session.get(DASHBOARD_SESSION_KEY):
        return redirect(url_for("dashboard.queue_monitor_page"))

    error_message = ""
    if request.method == "POST":
        username = request.form.get("username", "")
        password = request.form.get("password", "")
        username_matches = compare_digest(
            username,
            DASHBOARD_USERNAME,
        )
        password_matches = compare_digest(
            password,
            DASHBOARD_PASSWORD,
        )
        if username_matches and password_matches:
            session.clear()
            session[DASHBOARD_SESSION_KEY] = True
            return redirect(url_for("dashboard.queue_monitor_page"))
        error_message = "账号或密码错误，请重新输入"

    return render_template(
        "dashboard_login.html",
        error_message=error_message,
    )


@dashboard_app.route("/queue/logout", methods=["GET"])
def dashboard_logout():
    session.clear()
    return redirect(url_for("dashboard.dashboard_login"))


@dashboard_app.route("/api/queue/status", methods=["GET"])
def queue_status():
    data = {}

    platforms = [
        "BDAI", "DB", "DOUBA", "DP", "DPA", "DYAI",
        "KIMI", "TXYB", "TXYBA", "TYQW", "TYQWA", "WXYY", "XHSA"
    ]

    for platform in platforms:
        stream_key = f"{platform}:geo:stream_batch:list"
        batch_key = f"{platform}:geo:batch:list"

        data[stream_key] = redis_client.llen(stream_key)
        data[batch_key] = redis_client.llen(batch_key)

    data["geo:task_commit:list"] = redis_client.llen("geo:task_commit:list")

    return jsonify(data)


@dashboard_app.route("/queue/monitor", methods=["GET"])
def queue_monitor_page():
    return render_template("queue_monitor.html")


@dashboard_app.route("/api/queue/platformStats", methods=["GET"])
def get_req_trend():
    begin = request.args.get("begin", "").strip()
    end = request.args.get("end", "").strip()

    if not begin or not end:
        return jsonify({
            "code": 400,
            "msg": "begin and end are required",
            "data": []
        }), 400

    try:
        begin_date = datetime.strptime(begin, "%Y-%m-%d")
        end_date = datetime.strptime(end, "%Y-%m-%d")

        if begin_date > end_date:
            return jsonify({
                "code": 400,
                "msg": "begin must be earlier than end",
                "data": []
            }), 400
    except ValueError:
        return jsonify({
            "code": 400,
            "msg": "begin and end must be YYYY-MM-DD",
            "data": []
        }), 400

    try:
        rows = bh_utils.query_data(
            """
            SELECT
                platform,
                pt,
                uniqExactIf(reqId, channel IS NULL) AS system_count,
                uniqExactIf(reqId, channel IS NOT NULL)
                    AS third_party_count,
                uniqExact(reqId) AS task_count
            FROM geo_commit_task
            WHERE pt BETWEEN %s AND %s
            GROUP BY platform, pt
            ORDER BY pt, platform
            """,
            (
                begin_date.strftime("%Y%m%d"),
                end_date.strftime("%Y%m%d"),
            ),
        )
        if rows is None:
            raise RuntimeError("query geo_commit_task failed")

        data = [
            {
                "platform": row.get("platform"),
                "time": datetime.strptime(
                    str(row.get("pt")),
                    "%Y%m%d",
                ).strftime("%Y-%m-%d"),
                "system_count": int(
                    row.get("system_count") or 0
                ),
                "third_party_count": int(
                    row.get("third_party_count") or 0
                ),
                "count": int(row.get("task_count") or 0),
            }
            for row in rows
        ]
        return jsonify({
            "code": 200,
            "msg": "success",
            "data": data,
        })
    except (TypeError, ValueError, RuntimeError) as e:
        return jsonify({
            "code": 500,
            "msg": f"platformStats query failed: {e}",
            "data": []
        }), 500


@dashboard_app.route("/api/queue/thirdPartyStats", methods=["GET"])
def get_third_party_stats():
    begin = request.args.get("begin", "").strip()
    end = request.args.get("end", "").strip()
    selected_channel = request.args.get("channel", "").strip()
    selected_platform = request.args.get("platform", "").strip()

    if not begin or not end:
        return jsonify({
            "code": 400,
            "msg": "begin and end are required",
            "data": {},
        }), 400

    try:
        begin_date = datetime.strptime(begin, "%Y-%m-%d")
        end_date = datetime.strptime(end, "%Y-%m-%d")
        if begin_date > end_date:
            return jsonify({
                "code": 400,
                "msg": "begin must be earlier than end",
                "data": {},
            }), 400
    except ValueError:
        return jsonify({
            "code": 400,
            "msg": "begin and end must be YYYY-MM-DD",
            "data": {},
        }), 400

    begin_pt = begin_date.strftime("%Y%m%d")
    end_pt = end_date.strftime("%Y%m%d")
    day_count = (end_date - begin_date).days + 1

    try:
        enabled_channels = bh_utils.query_data(
            """
            SELECT
                channel,
                remark,
                daily_limit,
                total_limit,
                allow_platforms
            FROM geo_third_token
            WHERE status = 1
            ORDER BY channel
            """
        )
        if enabled_channels is None:
            raise RuntimeError("query enabled third channels failed")

        enabled_channel_names = [
            str(row.get("channel") or "").strip()
            for row in enabled_channels
            if str(row.get("channel") or "").strip()
        ]
        enabled_channel_set = set(enabled_channel_names)
        if (
            selected_channel
            and selected_channel not in enabled_channel_set
        ):
            return jsonify({
                "code": 400,
                "msg": "selected channel is not enabled",
                "data": {},
            }), 400

        if not enabled_channel_names:
            return jsonify({
                "code": 200,
                "msg": "success",
                "data": {
                    "metrics": {
                        "third_total": 0,
                        "daily_average": 0,
                        "third_ratio": 0,
                        "share_total": 0,
                        "enabled_channel_count": 0,
                    },
                    "channels": [],
                    "platforms": [],
                    "daily": [],
                    "channel_ranking": [],
                    "matrix": [],
                    "channel_details": [],
                },
            })

        placeholders = ", ".join(
            ["%s"] * len(enabled_channel_names)
        )
        usage_filters = [
            "pt BETWEEN %s AND %s",
            f"channel IN ({placeholders})",
        ]
        usage_params = [
            begin_pt,
            end_pt,
            *enabled_channel_names,
        ]
        if selected_channel:
            usage_filters.append("channel = %s")
            usage_params.append(selected_channel)
        if selected_platform:
            usage_filters.append("platform = %s")
            usage_params.append(selected_platform)
        usage_where = " AND ".join(usage_filters)

        usage_rows = bh_utils.query_data(
            f"""
            SELECT
                channel,
                platform,
                pt,
                uniqExact(reqId) AS task_count,
                uniqExactIf(
                    reqId,
                    ifNull(toString(is_share), '0') = '1'
                ) AS share_count
            FROM geo_commit_task
            WHERE {usage_where}
            GROUP BY channel, platform, pt
            ORDER BY pt, channel, platform
            """,
            tuple(usage_params),
        )
        if usage_rows is None:
            raise RuntimeError("query third party usage failed")

        summary_rows = bh_utils.query_data(
            f"""
            SELECT
                uniqExact(reqId) AS third_total,
                uniqExactIf(
                    reqId,
                    ifNull(toString(is_share), '0') = '1'
                ) AS share_total
            FROM geo_commit_task
            WHERE {usage_where}
            """,
            tuple(usage_params),
        )
        if summary_rows is None:
            raise RuntimeError("query third party summary failed")

        total_filters = ["pt BETWEEN %s AND %s"]
        total_params = [begin_pt, end_pt]
        if selected_platform:
            total_filters.append("platform = %s")
            total_params.append(selected_platform)
        total_rows = bh_utils.query_data(
            f"""
            SELECT uniqExact(reqId) AS all_total
            FROM geo_commit_task
            WHERE {" AND ".join(total_filters)}
            """,
            tuple(total_params),
        )
        if total_rows is None:
            raise RuntimeError("query all task summary failed")

        third_total = int(
            (summary_rows[0] if summary_rows else {}).get(
                "third_total"
            )
            or 0
        )
        share_total = int(
            (summary_rows[0] if summary_rows else {}).get(
                "share_total"
            )
            or 0
        )
        all_total = int(
            (total_rows[0] if total_rows else {}).get(
                "all_total"
            )
            or 0
        )

        daily_map = {}
        channel_map = {}
        matrix_map = {}
        platform_set = set()
        for row in usage_rows:
            channel = str(row.get("channel") or "")
            platform = str(row.get("platform") or "")
            pt = str(row.get("pt") or "")
            task_count = int(row.get("task_count") or 0)
            share_count = int(row.get("share_count") or 0)
            platform_set.add(platform)

            daily_key = (channel, pt)
            daily_map[daily_key] = (
                daily_map.get(daily_key, 0) + task_count
            )

            channel_item = channel_map.setdefault(
                channel,
                {
                    "channel": channel,
                    "task_count": 0,
                    "share_count": 0,
                    "platforms": set(),
                    "last_date": "",
                },
            )
            channel_item["task_count"] += task_count
            channel_item["share_count"] += share_count
            channel_item["platforms"].add(platform)
            if not channel_item["last_date"] or (
                pt > channel_item["last_date"]
            ):
                channel_item["last_date"] = pt

            matrix_key = (channel, platform)
            matrix_map[matrix_key] = (
                matrix_map.get(matrix_key, 0) + task_count
            )

        config_map = {
            str(row.get("channel") or ""): row
            for row in enabled_channels
        }
        ranking = []
        details = []
        channels_to_show = (
            [selected_channel]
            if selected_channel
            else enabled_channel_names
        )
        for channel in channels_to_show:
            config = config_map.get(channel) or {}
            usage = channel_map.get(channel) or {
                "task_count": 0,
                "share_count": 0,
                "platforms": set(),
                "last_date": "",
            }
            task_count = int(usage["task_count"])
            item = {
                "channel": channel,
                "remark": config.get("remark") or "",
                "task_count": task_count,
                "share_count": int(usage["share_count"]),
                "ratio": round(
                    task_count / third_total * 100,
                    2,
                ) if third_total else 0,
                "platform_count": len(usage["platforms"]),
                "last_date": usage["last_date"],
                "daily_limit": int(
                    config.get("daily_limit") or 0
                ),
                "total_limit": int(
                    config.get("total_limit") or 0
                ),
                "allow_platforms": (
                    config.get("allow_platforms") or "[]"
                ),
            }
            ranking.append(item)
            details.append(dict(item))

        ranking.sort(
            key=lambda item: item["task_count"],
            reverse=True,
        )
        details.sort(
            key=lambda item: item["task_count"],
            reverse=True,
        )
        platforms = sorted(platform_set)
        daily = [
            {
                "channel": channel,
                "pt": pt,
                "count": int(task_count),
            }
            for (channel, pt), task_count in sorted(
                daily_map.items()
            )
        ]
        matrix = [
            {
                "channel": channel,
                "platform": platform,
                "count": int(
                    matrix_map.get((channel, platform), 0)
                ),
            }
            for channel in channels_to_show
            for platform in platforms
        ]

        return jsonify({
            "code": 200,
            "msg": "success",
            "data": {
                "metrics": {
                    "third_total": third_total,
                    "daily_average": round(
                        third_total / day_count,
                        2,
                    ),
                    "third_ratio": round(
                        third_total / all_total * 100,
                        2,
                    ) if all_total else 0,
                    "share_total": share_total,
                    "enabled_channel_count": len(
                        enabled_channel_names
                    ),
                },
                "channels": [
                    {
                        "channel": row.get("channel"),
                        "remark": row.get("remark") or "",
                    }
                    for row in enabled_channels
                ],
                "platforms": platforms,
                "daily": daily,
                "channel_ranking": ranking,
                "matrix": matrix,
                "channel_details": details,
            },
        })
    except (TypeError, ValueError, RuntimeError) as exc:
        return jsonify({
            "code": 500,
            "msg": f"thirdPartyStats query failed: {exc}",
            "data": {},
        }), 500
