"""
用来计算健康雷达指数评分、用电健康指数评分
"""
from math import sqrt
import pendulum
import datetime
from pot_libs.logger import log
from pot_libs.mysql_util.mysql_util import MysqlUtil
from unify_api.modules.common.dao.health_score_dao import \
    get_point_dats_dao, get_mean_datas_dao
from unify_api.modules.common.procedures.points import load_compy_points
from unify_api.constants import FREQ_STANDARD
from unify_api.modules.home_page.procedures.dev_grade import get_dev_score


async def load_health_radar(cid, param_point_id=None):
    # 计算最近7天时间起始
    today = datetime.date.today()
    start_time = (today - datetime.timedelta(days=7)).strftime(
        "%Y-%m-%d %H:%M:%S")
    end_time = (today - datetime.timedelta(days=1)).strftime(
        "%Y-%m-%d 23:59:59")
    points_datas = await get_point_dats_dao(cid, param_point_id)
    items_2 = ("uab_mean", "freq_mean", "ubl_mean", "costtl_mean",
               "thduab_mean", "lf_mean")
    items_3 = ("ua_mean", "freq_mean", "ubl_mean", "costtl_mean",
               "thdua_mean", "lf_mean")
    points_datas = {point["pid"]: point for point in points_datas}
    pids_2, pids_3 = [], []
    for pid, value in points_datas.items():
        if value["ctnum"] == 2:
            pids_2.append(pid)
        else:
            pids_3.append(pid)
    datas_2 = await get_mean_datas_dao(pids_2, items_2, start_time, end_time)
    datas_3 = await get_mean_datas_dao(pids_3, items_3, start_time, end_time)
    datas = {data["pid"]: data for data in list(datas_2) + list(datas_3)}
    if datas and points_datas:
        for p_pid, p_value in points_datas.items():
            for d_pid, d_value in datas.items():
                if p_pid == d_pid:
                    p_value.update(d_value)
    # 电压偏差评分
    total, total_score = 0, 0
    for index, data in points_datas.items():
        ua_mean = data.get("ua_mean")
        if not ua_mean:
            continue
        meter_vc, ctnum = data.get("vc"), data.get("ctnum") or 3
        if meter_vc:
            stand_voltage = meter_vc / sqrt(3) if ctnum == 3 else meter_vc
        else:
            stand_voltage = 400 if ctnum == 3 else 10000
        v_dev = (ua_mean - stand_voltage) / stand_voltage
        score = get_dev_score(dev_type="v", cur=v_dev)
        if score is None:
            continue
        total_score += score
        total += 1
    v_score = total_score / total if total else 100
    # 频率偏差评分
    total, total_score = 0, 0
    for index, data in points_datas.items():
        freq_mean = data.get("freq_mean")
        if not freq_mean:
            continue
        freq_dev = freq_mean - FREQ_STANDARD
        score = get_dev_score(dev_type="freq", cur=freq_dev)
        if score is None:
            continue
        total_score += score
        total += 1
    freq_score = total_score / total if total else 100

    # 三相[电压]不平衡评分
    total, total_score = 0, 0
    for index, data in points_datas.items():
        ubl_avg = data.get("ubl_mean")
        if ubl_avg is None:
            continue
        score = get_dev_score(dev_type="ubl", cur=ubl_avg)
        if score is None:
            continue
        total_score += score
        total += 1
    ubl_score = total_score / total if total else 100

    # 功率因数：有进线级功率因数时，只计算进线级功率因数
    total, total_score = 0, 0
    for index, data in points_datas.items():
        costtl_mean = data.get("costtl_mean")
        if costtl_mean is None:
            continue
        score = get_dev_score(dev_type="costtl", cur=costtl_mean)
        if score is None:
            continue
        total_score += score
        total += 1
    costtl_score = total_score / total if total else 100

    # (电压)谐波畸变率
    # 电压谐波畸变：只计算三表法计量点，如果所有监测点都是二表法，则取其他所有指标均值
    total, total_score = 0, 0
    for index, data in points_datas.items():
        thdua_mean = data.get("thdua_mean")
        if thdua_mean is None:
            continue
        score = get_dev_score(dev_type="thdu", cur=thdua_mean)
        if score is None:
            continue
        total_score += score
        total += 1
    thdu_score = total_score / total if total else 100

    # 负载率
    total, total_score = 0, 0
    for index, data in points_datas.items():
        lf_mean = data.get("lf_mean")
        if lf_mean is None:
            score = 100
        else:
            score = get_dev_score(dev_type="lf", cur=lf_mean)
        if score is None:
            continue
        total_score += score
        total += 1
    lf_score = total_score / total if total else 100

    if not thdu_score:
        thdu_score = (v_score + freq_score + ubl_score + costtl_score +
                      lf_score) / 5.0
    score_info = {
        "v_score": round(v_score, 2),
        "freq_score": round(freq_score, 2),
        "ubl_score": round(ubl_score, 2),
        "costtl_score": round(costtl_score, 2),
        "thdu_score": round(thdu_score, 2),
        "lf_score": round(lf_score, 2),
    }
    return score_info


async def load_health_index(cid, point_id=None):
    """用电健康指数"""
    score_info = await load_health_radar(cid, point_id)

    if score_info is None:
        log.error("load_health_index fail")
        return 0

    v_score = score_info["v_score"]
    freq_score = score_info["freq_score"]
    if v_score <= 60 or freq_score <= 60:
        # 电压偏差/频率偏差评分，有一个低于60分，则整体健康指数为0
        log.info("v_score or freq_score less 60")
        return 0

    sub_dev = (1 - (v_score + freq_score) / 200.0) * 20
    sub_lf = (1 - score_info["lf_score"] / 100.0) * 20
    sub_costtl = (1 - score_info["costtl_score"] / 100.0) * 20
    sub_thdu = (1 - score_info["thdu_score"] / 100.0) * 20
    sub_ubl = (1 - score_info["ubl_score"] / 100.0) * 20
    return 100 - sub_dev - sub_lf - sub_costtl - sub_thdu - sub_ubl


async def load_manage_health_radar(cids, recent_days=30):
    """获取健康指数雷达-取15min数据
        注意:
        1.负载率的数据在写入ES15min数据时有做变压器判断逻辑，这里直接读取即可
        2.功率因数：有进线级功率因数时，只计算进线级功率因数
        3.电压谐波畸变：只计算三表法计量点，如果所有监测点都是二表法，则取其他所有指标均值
        """
    # 先从redis取，没有则重新计算，过期时间戳为当天0点
    # redis_key = "elec_score:health:%s" % cid
    # json_score = await RedisClient().get(redis_key)
    # if json_score:
    #     score_info = json.loads(json_score)
    #     return score_info

    # 计算最近30天时间起始
    today = pendulum.today()
    start_time = today.subtract(days=recent_days).format("YYYY-MM-DD HH:mm:ss")
    end_time = str(today.subtract(seconds=1)).format("YYYY-MM-DD HH:mm:ss")
    company_point_map = await load_compy_points(cids)
    all_point_map = dict()
    for cid, points in company_point_map.items():
        for pid, point_info in points.items():
            all_point_map[pid] = point_info
    all_point_ids = list(all_point_map.keys())
    sql = f"""
            select pid,avg(ua_mean) ua_mean,avg(uab_mean) uab_mean,avg(freq_mean) freq_mean,
            avg(ubl_mean) ubl_mean,avg(costtl_mean) costtl_mean,
            avg(thdua_mean) thdua_mean,avg(lf_mean) lf_mean from
            point_15min_electric
            where pid in %s and create_time >= %s and create_time <=%s
            group by pid
        """
    async with MysqlUtil() as conn:
        datas = await conn.fetchall(sql,
                                    args=(all_point_ids, start_time, end_time))
    data_map = {i['pid']: i for i in datas}

    # 单独计算每个公司的健康指数
    company_score_map = {}
    for cid in cids:
        point_map = company_point_map.get(cid)
        # 公司没有监测点
        if not point_map:
            # 默认没有监测点，各项分数都满分
            company_score_map[cid] = {
                "v_score": 100,
                "freq_score": 100,
                "ubl_score": 100,
                "costtl_score": 100,
                "thdu_score": 100,
                "lf_score": 100,
            }
            continue
        inline_point_ids, point_ids = [], []
        for point_id, point_item in point_map.items():
            if point_item["inlid"]:
                inline_point_ids.append(point_id)
            else:
                point_ids.append(point_id)

        # 1. 电压偏差评分
        total, total_score = 0, 0
        for point_id in inline_point_ids + point_ids:
            data_point_map = data_map.get(point_id)
            if not data_point_map:
                continue
            ua_mean = data_point_map.get("ua_mean")
            if ua_mean is None:
                continue
            point_info = all_point_map[point_id]
            meter_vc, ctnum = point_info.get("vc"), point_info.get(
                "ctnum") or 3
            if meter_vc:
                stand_voltage = meter_vc / sqrt(3) if ctnum == 3 else meter_vc
            else:
                stand_voltage = 400 if ctnum == 3 else 10000
            v_dev = (ua_mean - stand_voltage) / stand_voltage
            score = get_dev_score(dev_type="v", cur=v_dev)
            if score is None:
                continue

            total_score += score
            total += 1
        v_score = total_score / total if total else 100

        # 2. 频率偏差评分
        total, total_score = 0, 0
        for point_id in inline_point_ids + point_ids:
            data_point_map = data_map.get(point_id)
            if not data_point_map:
                continue
            freq_mean = data_point_map.get("freq_mean")
            if freq_mean is None:
                continue

            freq_dev = freq_mean - FREQ_STANDARD
            score = get_dev_score(dev_type="freq", cur=freq_dev)
            if score is None:
                continue
            total_score += score
            total += 1
        freq_score = total_score / total if total else 100

        # 3. 三相[电压]不平衡评分
        total, total_score = 0, 0
        for point_id in inline_point_ids + point_ids:
            data_point_map = data_map.get(point_id)
            if not data_point_map:
                continue
            ubl_avg = data_point_map.get("ubl_mean")
            if ubl_avg is None:
                continue
            score = get_dev_score(dev_type="ubl", cur=ubl_avg)
            if score is None:
                continue
            total_score += score
            total += 1
        ubl_score = total_score / total if total else 100

        # 4. 功率因数：有进线级功率因数时，只计算进线级功率因数
        total, total_score = 0, 0
        if inline_point_ids:
            ids = inline_point_ids
        else:
            ids = point_ids
        for point_id in ids:
            data_point_map = data_map.get(point_id)
            if not data_point_map:
                continue
            costtl_mean = data_point_map.get("costtl_mean")
            if costtl_mean is None:
                continue
            score = get_dev_score(dev_type="costtl", cur=costtl_mean)
            if score is None:
                continue
            total_score += score
            total += 1
        costtl_score = total_score / total if total else 100

        # 4.(电压)谐波畸变率
        # 电压谐波畸变：只计算三表法计量点，如果所有监测点都是二表法，则取其他所有指标均值
        total, total_score = 0, 0
        for point_id in inline_point_ids + point_ids:
            data_point_map = data_map.get(point_id)
            if not data_point_map:
                continue
            thdua_mean = data_point_map.get("thdua_mean")
            if thdua_mean is None:
                continue
            score = get_dev_score(dev_type="thdu", cur=thdua_mean)
            if score is None:
                continue
            total_score += score
            total += 1
        thdu_score = total_score / total if total else 100

        # 5. 负载率
        total, total_score = 0, 0
        for point_id in inline_point_ids + point_ids:
            data_point_map = data_map.get(point_id)
            if not data_point_map:
                continue
            lf_mean = data_point_map.get("lf_mean")
            if lf_mean is None:
                score = 100
            else:
                score = get_dev_score(dev_type="lf", cur=lf_mean)
            if score is None:
                continue
            total_score += score
            total += 1
        lf_score = total_score / total if total else 100
        log.info(
            "company_id:%s v_score:%s, freq_score:%s, ubl_score:%s, costtl_score:%s, "
            "thdu_score:%s, lf_score:%s",
            cid,
            v_score,
            freq_score,
            ubl_score,
            costtl_score,
            thdu_score,
            lf_score,
        )
        if not thdu_score:
            thdu_score = (
                                 v_score + freq_score + ubl_score + costtl_score + lf_score) / 5.0

        company_score_map[cid] = {
            "v_score": v_score,
            "freq_score": freq_score,
            "ubl_score": ubl_score,
            "costtl_score": costtl_score,
            "thdu_score": thdu_score,
            "lf_score": lf_score,
        }
    print("company_score_map = {}".format(company_score_map))
    return company_score_map


async def load_manage_health_index(company_score_info):
    """
    根据load_manage_health_radar函数返回的company_score_map计算公司的指数
    :param company_score_info:
    :return:
    """
    company_index_map = {}
    for cid, score_info in company_score_info.items():
        if score_info is None:
            log.error(f"cid = {cid}load_health_index fail")
            company_index_map[cid] = 0
            continue

        v_score = score_info["v_score"]
        freq_score = score_info["freq_score"]
        if v_score <= 60 or freq_score <= 60:
            # 电压偏差/频率偏差评分，有一个低于60分，则整体健康指数为0
            log.info(f"cid = {cid} v_score or freq_score less 60")
            company_index_map[cid] = 0
            continue

        sub_dev = (1 - (v_score + freq_score) / 200.0) * 20
        sub_lf = (1 - score_info["lf_score"] / 100.0) * 20
        sub_costtl = (1 - score_info["costtl_score"] / 100.0) * 20
        sub_thdu = (1 - score_info["thdu_score"] / 100.0) * 20
        sub_ubl = (1 - score_info["ubl_score"] / 100.0) * 20
        company_index_map[
            cid] = 100 - sub_dev - sub_lf - sub_costtl - sub_thdu - sub_ubl
    return company_index_map
