import random
from datetime import datetime, timedelta

from pot_libs.es_util.es_utils import EsUtil
from pot_libs.logger import log
from pot_libs.mysql_util.mysql_util import MysqlUtil
from pot_libs.utils.pendulum_wrapper import my_pendulum
from unify_api import constants
from unify_api.constants import COMPANY_1DAY_POWER, EVENT_TYPE_MAP, Importance
from unify_api.modules.alarm_manager.dao.list_static_dao import \
    sdu_alarm_aggs_type
from unify_api.modules.common.procedures.cids import get_cid_info
from unify_api.modules.common.procedures.common_cps import (
    proxy_safe_run_info,
    alarm_time_distribution,
)
from unify_api.modules.common.procedures.common_utils import get_electric_index
from unify_api.modules.common.procedures.health_score import (
    load_manage_health_radar,
    load_manage_health_index,
)
from unify_api.modules.common.procedures.points import get_points
from unify_api.modules.home_page.procedures.count_info_pds import \
    datetime_to_timestamp
from unify_api.utils.es_query_body import agg_statistics
from unify_api.utils.time_format import last30_day_range_today


async def proxy_alarm_score(cids):
    now = datetime.now()
    end_timestamp = datetime_to_timestamp(now)
    start_timestamp = datetime_to_timestamp(
        datetime(now.year, now.month, now.day) - timedelta(30))

    score_events = [
        i
        for i in EVENT_TYPE_MAP.keys()
        if i
           not in [
               "overTemp",
               "overTempRange1min",
               "overTempRange15min",
               "overTempTrendDaily",
               "overTempTrendQuarterly",
               "over_gap_u",
               "over_rms_u",
               "over_gap_i",
               "over_rms_i",
               "under_rms_u",
           ]
    ]
    query_body = {
        "query": {
            "bool": {
                "filter": [
                    {"terms": {"cid": cids}},
                    {"terms": {"type.keyword": score_events, }},
                    {"range": {"time": {"gte": start_timestamp,
                                        "lte": end_timestamp, }}},
                ],
            }
        },
        "size": 0,
        "aggs": {},
    }
    for cid in cids:
        query_body["aggs"][f"cid_{cid}_aggs"] = {
            "filter": {"term": {"cid": cid}},
            "aggs": {"importance": {"terms": {"field": "importance"}}},
        }

    log.info("cal_score_safe_electric query_body={}".format(query_body))
    async with EsUtil() as es:
        es_result = await es.search_origin(body=query_body,
                                           index=constants.POINT_1MIN_EVENT)

    cid_alarm_score_map = {}
    for cid in cids:
        cid_aggs_info = es_result.get("aggregations", {}).get(
            f"cid_{cid}_aggs", {})
        if not cid_aggs_info:
            cid_alarm_score_map["alarm_score"] = 0
            continue

        first_alarm_cnt = 0
        second_alarm_cnt = 0
        third_alarm_cnt = 0
        for bucket in cid_aggs_info.get("importance", {}).get("buckets", []):
            if bucket["key"] == Importance.First.value:
                first_alarm_cnt += bucket["doc_count"]
            elif bucket["key"] == Importance.Second.value:
                second_alarm_cnt += bucket["doc_count"]
            elif bucket["key"] == Importance.Third.value:
                third_alarm_cnt += bucket["doc_count"]
        company_point_map = await get_points(cids)
        point_len = len(company_point_map.get(cid) or {})
        alarm_score = (
            (
                    first_alarm_cnt * 2 + second_alarm_cnt * 1 + third_alarm_cnt * 0.5) / point_len
            if point_len
            else 0
        )
        if alarm_score >= 15:
            alarm_score = 15
        cid_alarm_score_map[cid] = alarm_score
    return cid_alarm_score_map


async def security_level_count(cids):
    cid_alarm_score_map = await proxy_alarm_score(cids)
    security_level_map = {
        "high_cnt": 0,
        "pretty_high_cnt": 0,
        "medium_cnt": 0,
        "pretty_low_cnt": 0,
        "security_cnt": 0,
    }
    for cid, alarm_score in cid_alarm_score_map.items():
        if alarm_score <= 1:
            security_level_map["security_cnt"] += 1
        elif alarm_score > 1 and alarm_score <= 2:
            security_level_map["pretty_low_cnt"] += 1
        elif alarm_score > 2 and alarm_score <= 5:
            security_level_map["medium_cnt"] += 1
        elif alarm_score > 5 and alarm_score <= 10:
            security_level_map["pretty_high_cnt"] += 1
        else:
            security_level_map["high_cnt"] += 1
    return security_level_map


async def alarm_percentage_count(cids):
    start, end = last30_day_range_today()
    importance_sql = f"""
        SELECT
            COUNT(*) doc_count,
            importance
        FROM
            point_1min_event pevent
        WHERE
            cid = %s
        AND pevent.event_datetime >= '{start}'
        AND pevent.event_datetime <= '{end}'
        GROUP BY
            pevent.importance
    """
    event_type_sql = f"""
        SELECT 
            COUNT(*) doc_count, 
            event_type
        FROM
            point_1min_event pevent
        WHERE
            cid = %s
        AND pevent.event_datetime >= '{start}'
        AND pevent.event_datetime <= '{end}'
        GROUP BY
            pevent.event_type;
    """
    async with MysqlUtil() as conn:
        event_type_data = await conn.fetchall(event_type_sql, args=(cids,))
        importance_data = await conn.fetchall(importance_sql, args=(cids,))

    first_alarm_cnt, second_alarm_cnt, third_alarm_cnt = 0, 0, 0
    for bucket in importance_data:
        if bucket["importance"] == Importance.First.value:
            first_alarm_cnt += bucket["doc_count"]
        elif bucket["importance"] == Importance.Second.value:
            second_alarm_cnt += bucket["doc_count"]
        elif bucket["importance"] == Importance.Third.value:
            third_alarm_cnt += bucket["doc_count"]

    temperature_cnt, residual_current_cnt, electric_param_cnt = 0, 0, 0
    for bucket in event_type_data:
        if bucket["event_type"] in [
            "overTemp",
            "overTempRange1min",
            "overTempRange15min",
            "overTempTrendDaily",
            "overTempTrendQuarterly",
        ]:
            temperature_cnt += bucket["doc_count"]
        elif bucket["event_type"] in [
            "overResidualCurrent",
        ]:
            residual_current_cnt += bucket["doc_count"]
        else:
            electric_param_cnt += bucket["doc_count"]

    time_distribution_map = await alarm_time_distribution(cids, start,
                                                          end)
    alarm_percentage_map = {
        "first_alarm_cnt": first_alarm_cnt,
        "second_alarm_cnt": second_alarm_cnt,
        "third_alarm_cnt": third_alarm_cnt,
        "temperature_cnt": temperature_cnt,
        "residual_current_cnt": residual_current_cnt,
        "electric_param_cnt": electric_param_cnt,
        "day_alarm_cnt": time_distribution_map["day_alarm_cnt"],
        "night_alarm_cnt": time_distribution_map["night_alarm_cnt"],
        "morning_alarm_cnt": time_distribution_map["morning_alarm_cnt"],
    }
    return alarm_percentage_map


async def alarm_percentage_count_old(cids):
    now = datetime.now()
    end_timestamp = datetime_to_timestamp(now)
    start_timestamp = datetime_to_timestamp(
        datetime(now.year, now.month, now.day) - timedelta(30))

    query_body = {
        "query": {
            "bool": {
                "filter": [
                    {"terms": {"cid": cids}},
                    {"range": {"time": {"gte": start_timestamp,
                                        "lte": end_timestamp, }}},
                ],
            }
        },
        "size": 0,
        "aggs": {
            "importance_aggs": {
                "filter": {"terms": {"cid": cids}},
                "aggs": {"importance": {
                    "terms": {"field": "importance", "size": 10000}}},
            },
            "type_aggs": {
                "filter": {"terms": {"cid": cids}},
                "aggs": {"type": {
                    "terms": {"field": "type.keyword", "size": 10000}}},
            },
        },
    }
    log.info("cal_score_safe_electric query_body={}".format(query_body))
    async with EsUtil() as es:
        es_result = await es.search_origin(body=query_body,
                                           index=constants.POINT_1MIN_EVENT)

    importance_buckets = (
        es_result.get("aggregations", {})
            .get("importance_aggs", {})
            .get("importance", {})
            .get("buckets", [])
    )
    first_alarm_cnt, second_alarm_cnt, third_alarm_cnt = 0, 0, 0
    for bucket in importance_buckets:
        if bucket["key"] == Importance.First.value:
            first_alarm_cnt += bucket["doc_count"]
        elif bucket["key"] == Importance.Second.value:
            second_alarm_cnt += bucket["doc_count"]
        elif bucket["key"] == Importance.Third.value:
            third_alarm_cnt += bucket["doc_count"]

    type_buckets = (
        es_result.get("aggregations", {}).get("type_aggs", {}).get("type",
                                                                   {}).get(
            "buckets", [])
    )

    temperature_cnt, residual_current_cnt, electric_param_cnt = 0, 0, 0
    for bucket in type_buckets:
        if bucket["key"] in [
            "overTemp",
            "overTempRange1min",
            "overTempRange15min",
            "overTempTrendDaily",
            "overTempTrendQuarterly",
        ]:
            temperature_cnt += bucket["doc_count"]
        elif bucket["key"] in [
            "overResidualCurrent",
        ]:
            residual_current_cnt += bucket["doc_count"]
        else:
            electric_param_cnt += bucket["doc_count"]

    start_dt_str = str(datetime.fromtimestamp(start_timestamp))
    end_dt_str = str(datetime.fromtimestamp(end_timestamp))
    time_distribution_map = await alarm_time_distribution(cids, start_dt_str,
                                                          end_dt_str)
    alarm_percentage_map = {
        "first_alarm_cnt": first_alarm_cnt,
        "second_alarm_cnt": second_alarm_cnt,
        "third_alarm_cnt": third_alarm_cnt,
        "temperature_cnt": temperature_cnt,
        "residual_current_cnt": residual_current_cnt,
        "electric_param_cnt": electric_param_cnt,
        "day_alarm_cnt": time_distribution_map["day_alarm_cnt"],
        "night_alarm_cnt": time_distribution_map["night_alarm_cnt"],
        "morning_alarm_cnt": time_distribution_map["morning_alarm_cnt"],
    }
    return alarm_percentage_map


async def proxy_today_alarm_cnt(cids, group_field="importance"):
    """
    代码复用，默认是按照importance字段分组
    :param cids:
    :param group_field: example: "importance"
    :return:
    """
    now = datetime.now()
    end_timestamp = datetime_to_timestamp(now)
    start_timestamp = datetime_to_timestamp(
        datetime(now.year, now.month, now.day))

    query_body = {
        "query": {
            "bool": {
                "filter": [
                    {"terms": {"cid": cids}},
                    {"range": {"time": {"gte": start_timestamp,
                                        "lte": end_timestamp, }}},
                ],
            }
        },
        "size": 0,
        "aggs": {},
    }

    int_group_field = [
        "importance",
    ]
    for cid in cids:
        query_body["aggs"][f"cid_{cid}_aggs"] = {
            "filter": {"term": {"cid": cid}},
            "aggs": {
                f"{group_field}": {
                    "terms": {
                        "field": f"{group_field}"
                        if group_field in int_group_field
                        else f"{group_field}.keyword",
                        "size": 10000,
                    }
                }
            },
        }
    log.info("alarm aggs query_body={}".format(query_body))
    async with EsUtil() as es:
        es_result = await es.search_origin(body=query_body,
                                           index=constants.POINT_1MIN_EVENT)

    cid_alarm_map = {
        cid: {
            "first_alarm_cnt": 0,
            "second_alarm_cnt": 0,
            "third_alarm_cnt": 0,
            "overuse_eleprod": 0,
            "high_p_eleprod": 0,
            "illegal_eleprod": 0,
            "electric_quantity": 0,
            "ele_car_battery": 0,
        }
        for cid in cids
    }
    for cid in cids:
        cid_buckets = (
            es_result.get("aggregations", {})
                .get(f"cid_{cid}_aggs", {})
                .get(group_field, {})
                .get("buckets", [])
        )
        alarm_type_map = {
            Importance.First.value: "first_alarm_cnt",
            Importance.Second.value: "second_alarm_cnt",
            Importance.Third.value: "third_alarm_cnt",
            "ele_overload": "overuse_eleprod",
            "high_power_app": "high_p_eleprod",
            "illegal_ele_app": "illegal_eleprod",
            "power_quality_low": "electric_quantity",
            "ele_car_battery": "ele_car_battery",
        }
        for bucket in cid_buckets:
            if bucket["key"] in alarm_type_map:
                _key = alarm_type_map[bucket["key"]]
                cid_alarm_map[cid][_key] += bucket["doc_count"]
    return cid_alarm_map


async def proxy_today_spfv_cnt(cids):
    """
    批量获取各工厂今日spvf用电
    :param cids:
    :return:
    """
    now = datetime.now()
    start_time = datetime(now.year, now.month, now.day)
    end_time = datetime(now.year, now.month, now.day, now.hour, now.minute,
                        now.second)
    es_start = datetime.strftime(start_time, "%Y-%m-%dT%H:%M:%S+08:00")
    es_end = datetime.strftime(end_time, "%Y-%m-%dT%H:%M:%S+08:00")

    query_body = {
        "query": {
            "bool": {
                "filter": [
                    {"terms": {"cid": cids}},
                    {"range": {
                        "quarter_time": {"gte": es_start, "lte": es_end}}},
                ]
            }
        },
        "size": 0,
        "aggs": {
            "cid_aggs": {
                # 注意这里size不设置的话，只会返回结果聚合结果10条，也就是cids最多返回10个
                "terms": {"field": "cid", "size": 10000},
                "aggs": {
                    "spfv_aggs": {
                        "terms": {"field": "spfv.keyword"},
                        "aggs": {"kwh": {"sum": {"field": "kwh"}}},
                    }
                },
            }
        },
    }

    log.info("spfv aggs query_body={}".format(query_body))
    async with EsUtil() as es:
        es_result = await es.search_origin(body=query_body,
                                           index=constants.COMPANY_15MIN_POWER)

    cid_buckets = es_result.get("aggregations", {}).get("cid_aggs", {}).get(
        "buckets", [])

    cid_spfv_map = {cid: {"s": 0, "p": 0, "f": 0, "v": 0} for cid in cids}

    for bucket in cid_buckets:
        cid = bucket.get("key")
        spvf_buckets = bucket.get("spfv_aggs", {}).get("buckets", [])
        for i in spvf_buckets:
            cid_spfv_map[cid][i["key"]] += round(i["kwh"]["value"])
    log.info(f"cid_spfv_map = {cid_spfv_map}")
    return cid_spfv_map


async def proxy_map_info(cids):
    """
    获取首页代理信息
    :param cids:
    :return:
    """
    # 1. 今日报警统计
    cid_alarm_map = await proxy_today_alarm_cnt(cids)

    # 2. 今日用电
    cid_power_spfv_map = await proxy_today_spfv_cnt(cids)

    # 3. 安全运行天数
    cid_safe_run_map = await proxy_safe_run_info(cids)

    # 4. 安全排名
    cid_alarm_score_map = await proxy_alarm_score(cids)
    electric_index_map = {
        cid: get_electric_index(score) for cid, score in
        cid_alarm_score_map.items()
    }
    electric_index_list = sorted(
        [(round(i), cid) for cid, i in electric_index_map.items()],
        reverse=True
    )

    # 5. 健康排名
    company_score_map = await load_manage_health_radar(cids, recent_days=7)
    company_index_map = await load_manage_health_index(company_score_map)
    health_index_list = sorted(
        [(round(i), cid) for cid, i in company_index_map.items()], reverse=True
    )

    # 6. 组装返回数据
    cid_info_map = {cid: {} for cid in cids}
    async with MysqlUtil() as conn:
        company_sql = "select cid, shortname, longitude, latitude from company where cid in %s"
        companys = await conn.fetchall(company_sql, args=(cids,))
        company_map = {i["cid"]: i for i in companys}

    print(f"company_index_map = {company_index_map}")
    for cid in cids:
        cid_info_map[cid]["reg_today_alarm_info"] = {}
        cid_info_map[cid]["today_alarm_info"] = cid_alarm_map[cid]
        cid_info_map[cid]["today_power_info"] = cid_power_spfv_map[cid]
        cid_info_map[cid]["safe_run_days"] = cid_safe_run_map[cid][
            "safe_run_days"]
        cid_info_map[cid]["safe_rank"] = (
                electric_index_list.index(
                    (round(electric_index_map[cid]), cid)) + 1
        )
        cid_info_map[cid]["electric_index"] = round(electric_index_map[cid])
        cid_info_map[cid]["electric_score"] = cid_alarm_score_map[cid]
        cid_info_map[cid]["health_rank"] = (
                health_index_list.index(
                    (round(company_index_map[cid]), cid)) + 1
        )
        cid_info_map[cid]["health_index"] = round(company_index_map[cid])
        cid_info_map[cid]["company_cnt"] = len(cids)
        cid_info_map[cid]["company_name"] = company_map[cid]["shortname"]
        cid_info_map[cid]["today_alarm_cnt"] = cid_safe_run_map[cid][
            "today_alarm_count"]
        cid_info_map[cid]["longitude"] = company_map[cid].get(
            "longitude") or ""
        cid_info_map[cid]["latitude"] = company_map[cid].get("latitude") or ""

    return cid_info_map


async def reg_map_info(cids):
    """
    获取首页代理信息
    :param cids:
    :return:
    """
    # 1. 今日报警统计
    cid_alarm_map = await proxy_today_alarm_cnt(cids, group_field="type")

    # 2. 今日用电
    cid_power_spfv_map = await proxy_today_spfv_cnt(cids)

    # 3. 安全运行天数
    cid_safe_run_map = await proxy_safe_run_info(cids)

    # 4. 安全排名
    cid_alarm_score_map = await proxy_alarm_score(cids)
    electric_index_map = {
        cid: get_electric_index(score) for cid, score in
        cid_alarm_score_map.items()
    }
    electric_index_list = sorted(
        [(round(i), cid) for cid, i in electric_index_map.items()],
        reverse=True
    )

    # 6. 组装返回数据
    cid_info_map = {cid: {} for cid in cids}
    company_map = await get_cid_info(cids)

    for cid in cids:
        cid_info_map[cid]["reg_today_alarm_info"] = cid_alarm_map[cid]
        cid_info_map[cid]["today_alarm_info"] = {}
        cid_info_map[cid]["today_power_info"] = cid_power_spfv_map[cid]
        cid_info_map[cid]["safe_run_days"] = cid_safe_run_map[cid][
            "safe_run_days"]
        cid_info_map[cid]["safe_rank"] = (
                electric_index_list.index(
                    (round(electric_index_map[cid]), cid)) + 1
        )
        cid_info_map[cid]["electric_index"] = round(electric_index_map[cid])
        cid_info_map[cid]["electric_score"] = cid_alarm_score_map[cid]

        cid_info_map[cid]["company_cnt"] = len(cids)
        cid_info_map[cid]["company_name"] = company_map[cid]["shortname"]
        cid_info_map[cid]["today_alarm_cnt"] = cid_safe_run_map[cid][
            "today_alarm_count"]
        cid_info_map[cid]["longitude"] = company_map[cid].get(
            "longitude") or ""
        cid_info_map[cid]["latitude"] = company_map[cid].get("latitude") or ""

    return cid_info_map


async def total_run_day_proxy(cids):
    """总服务时长"""
    sql = "select create_time from company where cid in %s"
    async with MysqlUtil() as conn:
        result = await conn.fetchall(sql, (cids,))
    if not result:
        log.info(f"未找到工厂, cids:{cids}")
        return ""
    total_time = 0
    now_time = datetime.now()
    for res in result:
        create_time = res["create_time"]
        create_time = datetime.fromtimestamp(create_time)
        run_days = (now_time - create_time).days + 1
        total_time += run_days
    return total_time


async def alarm_safe_power(cid, start, end):
    """状态监测, 安全和用电, 智电u"""
    es_res = await sdu_alarm_aggs_type(cid, start, end)
    temperature_cnt, residual_current_cnt, lr_cnt, \
        power_factor_cnt, under_u_cnt, over_u_cnt, over_i_cnt \
        = 0, 0, 0, 0, 0, 0, 0

    for bucket in es_res:
        # 温度
        if bucket["event_type"] in ("overTemp", "overTempRange1min",
                             "overTempRange15min"):
            temperature_cnt += bucket["doc_count"]
        # 漏电流
        elif bucket["event_type"] in ("overResidualCurrent",):
            residual_current_cnt += bucket["doc_count"]
        # 负载率
        elif bucket["event_type"] in ("overPR",):
            lr_cnt += bucket["doc_count"]
        # 功率因数
        elif bucket["event_type"] in ("underPhasePF", "underTotalPF"):
            power_factor_cnt += bucket["doc_count"]
        # 欠压
        elif bucket["event_type"] in ("underU",):
            under_u_cnt += bucket["doc_count"]
        # 过压
        elif bucket["event_type"] in ("overU",):
            over_u_cnt += bucket["doc_count"]
        # 过流
        elif bucket["event_type"] in ("overI",):
            over_i_cnt += bucket["doc_count"]

    alarm_map = {
        "temperature_cnt": temperature_cnt,
        "residual_current_cnt": residual_current_cnt,
        "lr_cnt": lr_cnt,
        "power_factor_cnt": power_factor_cnt,
        "under_u_cnt": under_u_cnt,
        "over_u_cnt": over_u_cnt,
        "over_i_cnt": over_i_cnt,
    }
    return alarm_map
