import json
import io
import pandas as pd
from unify_api.modules.zhiwei_u.fault_foreast.actionFile import actionFilemin
from unify_api.modules.zhiwei_u import config
from unify_api.utils import time_format
from pot_libs.utils.exc_util import DBException
from dataclasses import fields

from unify_api.modules.zhiwei_u.dao.data_es_dao import query_search_scope, \
    query_search_scope_pids
from unify_api.modules.zhiwei_u.dao.data_es_dao import get_scope_pids, \
    get_search_scope

from unify_api.modules.zhiwei_u.dao.order_operations_dao import \
    select_cname_by_cid
from unify_api.modules.zhiwei_u.dao.scope_operations_dao import \
    sid_to_order_dao, select_sid_by_pid, select_cids_by_prod_id, \
    select_cname_by_cids, select_sid_by_pids
from unify_api.modules.zhiwei_u.components.scope_operations_cps import \
    SearchScopeResq, Alarm
from pot_libs.common.components.query import (
    PageRequest,
    Equal,
    Filter
)
from unify_api.modules.zhiwei_u.dao.warning_operations_dao import get_username
from pot_libs.common.components.responses import Success
from unify_api.modules.electric.procedures.electric_util import get_wiring_type
from pot_libs.es_util.es_utils import EsUtil
from pot_libs.es_util.es_query import EsQuery
from pot_libs.logger import log
from unify_api.modules.zhiwei_u.components.scope_operations_cps import \
    ScopeDetailsResponse, ScopeContent
from unify_api.modules.zhiwei_u.fault_foreast.test import leakage_reg
from unify_api.modules.scope_analyse.service.scope_analyse_service import \
    get_threhold


async def search_scope_service(prod_id, cid, pid, sid, page_num, page_size,
                               start, end):
    start_num = (page_num - 1) * page_size
    if not pid and sid:
        pids_datas = await sid_to_order_dao(sid)
        pids = [data["pid"] for data in pids_datas]
        # todo databases should be point_1min_scope, not point_1min_event
        scope_datas = await get_scope_pids(pids, start, end)

        datas = scope_datas[start_num: start_num+page_size]
        total = len(scope_datas)
        # datas = await query_search_scope_pids(pids, page_num, page_size,
        #                                       start, end)
    else:
        if prod_id and not cid and not pid:
            cid_list = await select_cids_by_prod_id(prod_id)
            cid = [c["cid"] for c in cid_list]
        elif cid:
            cid = [cid]
        scope_datas = await get_search_scope(cid, pid, start, end)
        datas = scope_datas[start_num: start_num + page_size]
        total = len(scope_datas)

    if not datas:
        return SearchScopeResq(total=0, rows=[])
    pids, cids = [], []
    for info in datas:
        pids.append(info["pid"])
        cids.append(info["cid"])
    companys = await select_cname_by_cids(list(set(cids)))
    points = await select_sid_by_pids(list(set(pids)))
    rows = []
    for info in datas:
        cid = info["cid"]
        for index, c in enumerate(companys):
            if cid == c["cid"]:
                company = companys[index]
                break
        else:
            company = []
        pid = info["pid"]
        message = info["message"]
        check_dt = info["event_datetime"]
        dt = time_format.convert_to_dt(check_dt)
        check_dt = time_format.convert_dt_to_timestr(dt)
        for index, p in enumerate(points):
            if pid == p["pid"]:
                point = points[index]
                break
        else:
            point = []
        doc_id = info["_id"]
        alarm = Alarm(check_dt=check_dt, prod_id=company.get("product"),
                      product=config.PRODUCT[company.get("product")],
                      cid=cid, shortname=company.get("shortname"),
                      pid=pid,  point=point.get("name"),  sid=point.get("sid"),
                      message=message, doc_id=doc_id
                      )
        rows.append(alarm)
    return SearchScopeResq(total=total, rows=rows)


async def scope_detail_service(id, wave_range, download=None, user_id=None):

    # 1.根据es_id查询point_1min_event对应的point属性
    es_id = Equal(field="_id", value=id)
    filter = Filter(equals=[es_id], ranges=[], in_groups=[], keywords=[])
    query_request = PageRequest(page_size=1, page_num=1, filter=filter,
                                sort=None)
    query_body = EsQuery.query(query_request)
    try:
        async with EsUtil() as es:
            es_results = await es.search_origin(
                body=query_body,
                index=config.SCOPE_DATABASE)
    except:
        log.error("es query error")
        return ScopeDetailsResponse().db_error()

    try:
        es_results = es_results["hits"]["hits"][0]
        source = es_results.get("_source", {})
        point_name = source.get("name")
        point_id = source.get("point_id")
        cmp_time = source.get("time")

        cid = source.get("cid")
        company = await select_cname_by_cid(cid)
        message = source.get("message")
        check_dt = source.get("datetime")
        dt = time_format.convert_to_dt(check_dt)
        check_dt = time_format.convert_dt_to_timestr(dt)
        point = await select_sid_by_pid(point_id)

    except Exception:
        log.warning("can not find data on es(index: %s): %s" % (
            config.POINT_1MIN_SCOPE, query_body))
        raise DBException
    # 4.接线法:二表/三表
    ctnum, _ = await get_wiring_type(point_id)
    if not ctnum:
        ctnum = 3

    # 2.用point_1min_event的ponint_id、cmp_time查询point_1min_scope
    point_equal = Equal(field="point_id", value=point_id)
    time_equal = Equal(field="time", value=cmp_time)
    filter = Filter(equals=[point_equal, time_equal], ranges=[], in_groups=[],
                    keywords=[])
    query_request = PageRequest(page_size=1, page_num=1, filter=filter,
                                sort=None)
    query_body = EsQuery.query(query_request)
    try:
        async with EsUtil() as es:
            es_results = await es.search_origin(
                body=query_body,
                index=config.POINT_1MIN_SCOPE)
    except Exception as e:
        log.error(f"es query error {e}")
        return ScopeDetailsResponse().db_error()

    if not es_results:
        log.warning("can not find data on es(index: %s): %s" % (
            config.POINT_1MIN_SCOPE, query_body))
        return ScopeDetailsResponse().db_error()

    if not es_results["hits"]["hits"]:

        alarm = Alarm(check_dt=check_dt, pid=point_id, cid=cid,
                      point=point_name, sid=point["sid"], message=message,
                      prod_id=company["product"],
                      product=config.PRODUCT[company["product"]],
                      shortname=company["shortname"], contin_time=0,
                      result="暂无数据")
        return ScopeDetailsResponse(
            alarm=alarm,
            ctnum=ctnum,
            group=point_name,
            item={},
            scope_type="",
            type="",
            date_time="",
            contents={}
        )
    es_results = es_results["hits"]["hits"][0]
    scope_type = es_results.get("_source", {}).get("fault_type")
    fault_type = config.EVENT_TYPE_MAP.get(scope_type)
    date_time = time_format.esstr_to_dtstr(
        es_results["_source"].get("datetime"))
    position = list(json.loads(es_results["_source"].get("result")).keys())
    position_tmp = [
        pos + "相" if len(pos) == 1 else pos + "线" if len(pos) == 2 else '' for
        pos in position]
    position_tmp = [p for p in position_tmp if p]  # 去掉空字符串
    position_str = "|".join(position_tmp)

    # 3.曲线数据
    res_dic = json.loads(es_results["_source"].get("result"))
    location = [d for d in res_dic.values()][0].get("location")
    wave_data = json.loads(es_results["_source"].get("context"))
    length = wave_data.get("ia") or wave_data.get("ileak_rms")
    contin_time = len(length)
    if wave_range == "100ms":
        if location <= 200:
            for key, value in wave_data.items():
                wave_data[key] = value[:400]
        elif location >= 1400:
            for key, value in wave_data.items():
                wave_data[key] = value[1200:]
        else:
            for key, value in wave_data.items():
                wave_data[key] = value[location - 200:location + 200]
    scope_content = ScopeContent(
        **{k: v for k, v in wave_data.items() if
           k in [field.name for field in fields(ScopeContent)]}
    )
    # 下载报表
    if download:
        download_auth = None
        if user_id:
            user_info = await get_username(user_id)
            download_auth = user_info.get("role", None)
        if scope_type == "over_res_cur":
            wave_data = {
                "序号": [i for i in range(1, contin_time + 1)],
                "ileak_rms": wave_data.get("ileak_rms"),
                "ileak": wave_data.get("ileak"),
            }
            table_name = f'{company["shortname"]}_{point_name}_' \
                         f'{check_dt[:10]}_漏电流录波'
            return await excl_download(wave_data, table_name, "sheet1")
        elif download_auth == 2:
            wave_data = {
                "序号": [i for i in range(1, contin_time + 1)],
                "ia": wave_data.get("ia"),
                "ib": wave_data.get("ib"),
                "ic": wave_data.get("ic"),
                "ua": wave_data.get("ua"),
                "ub": wave_data.get("ub"),
                "uc": wave_data.get("uc"),
            }
            table_name = f'{company["shortname"]}_{point_name}_' \
                         f'{check_dt[:10]}_电压录波/电流录波'
            return await excl_download(wave_data, table_name, "sheet1")
        else:
            return Success(success=0, message="您的权限不足")
    # 结论分析
    try:
        log.warning(f"wave_data:{wave_data}")
        if scope_type == "over_res_cur":
            threhold = await get_threhold(cid, point["sid"])
            result = leakage_reg(ileak_rms=wave_data["ileak_rms"],
                                 leak_hold=threhold)
            log.info(f"actionFile 漏电流 sid:{point['sid']}结论:{result}")
        else:
            res = actionFilemin(wave_data, ctnum)
            log.warning(f"结论分析 res:{res}")
            if res == "nofault":
                result = "不存在故障"
            else:
                result = ""
                for r in res:
                    result += "%s : %0.0f%%;" % (r[0], r[1]*100)
    except Exception as e:
        result = ""
        log.error(f"actionFile error：{e}")
    alarm = Alarm(check_dt=check_dt, pid=point_id, cid=cid,
                  point=point_name, sid=point["sid"], message=message,
                  prod_id=company["product"],
                  product=config.PRODUCT[company["product"]],
                  shortname=company["shortname"],
                  contin_time=int(contin_time * 0.25),
                  result=result)

    return ScopeDetailsResponse(
        alarm=alarm,
        ctnum=ctnum,
        group=point_name,
        item=position_str,
        scope_type=scope_type,
        type=fault_type,
        date_time=date_time,
        location=location,
        contents=scope_content
    )


async def excl_download(dict_data, table_name, sheet_name):
    output = io.BytesIO()
    excel_name = table_name + '.xlsx'
    writer = pd.ExcelWriter(output, engine='xlsxwriter')
    df = pd.DataFrame(dict_data)
    df.to_excel(writer, sheet_name=sheet_name, index=False)
    writer.save()
    output.seek(0)
    return output, excel_name


async def dataframe_excl_download(data, table_name, sheet_name="sheet1"):
    output = io.BytesIO()
    excel_name = table_name + '.xlsx'
    writer = pd.ExcelWriter(output, engine='xlsxwriter')
    data.to_excel(writer, sheet_name=sheet_name, index=False)
    writer.save()
    output.seek(0)
    return output, excel_name
