from datetime import datetime
import pendulum
from pot_libs.logger import log
from pot_libs.mysql_util.mysql_util import MysqlUtil
from unify_api.constants import EVENT_TYPE_MAP, Importance, CST
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 load_compy_points
from unify_api.utils.time_format import last30_day_range_today


async def proxy_alarm_score(cids):
    now_dt = pendulum.now()
    end_time = now_dt.format("YYYY-MM-DD HH:mm:ss")
    start_time = now_dt.subtract(days=30).format("YYYY-MM-DD HH:mm:ss")
    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",
           ]
    ]
    sql = f"""
        select cid,importance,count(*) count from point_1min_event
        where cid in %s and event_datetime >=%s and event_datetime <= %s
        and event_type in %s
        group by cid,importance
    """
    log.info("cal_score_safe_electric sql={}".format(sql))
    async with MysqlUtil() as conn:
        datas = await conn.fetchall(sql, args=(
            cids, start_time, end_time, score_events))
    data_map = {"{}-{}".format(i["cid"], i["importance"]): i["count"] for i in
                datas}

    cid_alarm_score_map = {}
    for cid in cids:
        first_key = "{}-{}".format(cid, Importance.First.value)
        second_key = "{}-{}".format(cid, Importance.Second.value)
        third_key = "{}-{}".format(cid, Importance.Third.value)
        if first_key not in data_map and second_key not in data_map and \
                third_key not in data_map:
            cid_alarm_score_map[cid] = 100
            continue
        first_alarm_cnt = 0
        second_alarm_cnt = 0
        third_alarm_cnt = 0
        if first_key in data_map:
            first_alarm_cnt = data_map.get(first_key)
        if second_key in data_map:
            second_alarm_cnt = data_map.get(second_key)
        if third_key in data_map:
            third_alarm_cnt = data_map.get(third_key)
        company_point_map = await load_compy_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 1 < alarm_score <= 2:
            security_level_map["pretty_low_cnt"] += 1
        elif 2 < alarm_score <= 5:
            security_level_map["medium_cnt"] += 1
        elif 5 < 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 in %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 in %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 proxy_today_alarm_cnt(cids, group_field="importance"):
    """
    代码复用，默认是按照importance字段分组
    :param cids:
    :param group_field: example: "importance"
    :return:
    """
    start_time = pendulum.now(tz=CST).start_of(unit="day").format(
        "YYYY-MM-DD HH:mm:ss")
    end_time = pendulum.now(tz=CST).format("YYYY-MM-DD HH:mm:ss")
    if group_field == "type":
        # 需要关联event_type里面的type
        group_field = "event_type"
    sql = f"""
        select cid,{group_field},count(*) count from
        point_1min_event
        where event_datetime >=%s and event_datetime <= %s
        and cid in %s
        group by cid,{group_field}
    """
    async with MysqlUtil() as conn:
        datas = await conn.fetchall(sql, args=(start_time, end_time, cids))

    log.info("alarm aggs sql={}".format(sql))

    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
    }
    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 data in datas:
        cid = data.get("cid")
        key = data.get(group_field)
        if key in alarm_type_map:
            value = alarm_type_map.get(key)
            cid_alarm_map[cid][value] = data.get("count")
    return cid_alarm_map


async def proxy_today_spfv_cnt(cids):
    """
    批量获取各工厂今日spvf用电
    :param cids:
    :return:
    """
    start_time = pendulum.now(tz=CST).start_of(unit="day").format(
        "YYYY-MM-DD HH:mm:ss")
    end_time = pendulum.now(tz=CST).format("YYYY-MM-DD HH:mm:ss")
    sql = f"""
     select cid,spfv,sum(kwh) kwh from company_15min_power
     where cid in %s and create_time >=%s and create_time <=%s
     group by cid,spfv
    """
    async with MysqlUtil() as conn:
        datas = await conn.fetchall(sql, args=(cids, start_time, end_time))

    cid_spfv_map = {cid: {"s": 0, "p": 0, "f": 0, "v": 0} for cid in cids}
    for data in datas:
        cid = data.get("cid")
        spfv = data.get("spfv")
        cid_spfv_map[cid][spfv] = data.get("kwh")
    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"""
    temp_cnt, over_rc_cnt, lr_cnt, pf_cnt = 0, 0, 0, 0
    under_u_cnt, over_u_cnt, over_i_cnt = 0, 0, 0
    es_res = await sdu_alarm_aggs_type(cid, start, end)
    for bucket in es_res:
        # 温度
        e_type = bucket["event_type"]
        if e_type in ("overTemp", "overTempRange1min", "overTempRange15min"):
            temp_cnt += bucket["doc_count"]
        # 漏电流
        elif e_type in ("overResidualCurrent",):
            over_rc_cnt += bucket["doc_count"]
        # 负载率
        elif e_type in ("overPR",):
            lr_cnt += bucket["doc_count"]
        # 功率因数
        elif e_type in ("underPhasePF", "underTotalPF"):
            pf_cnt += bucket["doc_count"]
        # 欠压
        elif e_type in ("underU",):
            under_u_cnt += bucket["doc_count"]
        # 过压
        elif e_type in ("overU",):
            over_u_cnt += bucket["doc_count"]
        # 过流
        elif e_type in ("overI",):
            over_i_cnt += bucket["doc_count"]

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