# -*- coding: utf-8 -*-

from enum import Enum

import arrow
import httpx
import numpy as np
import pandas as pd
from httpx import AsyncClient, URL

from app.core.config import settings
from app.services.service import Service
from app.utils.date import get_time_str, TIME_FMT
from app.utils.math import round_half_up


class Season(str, Enum):
    cooling = "Cooling"
    heating = "Warm"
    transition = "Transition"


class SpaceInfoService(Service):
    def __init__(
            self,
            client: AsyncClient,
            project_id: str,
            space_id: str,
            server_settings=settings,
    ) -> None:
        super(SpaceInfoService, self).__init__(client)
        self._project_id = project_id
        self._space_id = space_id
        self._base_url = URL(server_settings.TRANSFER_HOST)
        self._now_time = get_time_str()

    def _common_parameters(self) -> dict:
        return {"projectId": self._project_id, "spaceId": self._space_id}

    async def is_customized(self) -> bool:
        url = self._base_url.join("duoduo-service/custom-service/custom/timetarget")
        time_str = arrow.get(
            arrow.get(self._now_time, TIME_FMT).shift(minutes=15).timestamp()
            // 900
            * 900
        ).strftime("%Y%m%d%H%M%S")
        params = {
            "projectId": self._project_id,
            "objectId": self._space_id,
            "timepoint": time_str,
        }
        raw_info = await self._get(url, params)

        flag = False
        if raw_info.get("data"):
            flag = True

        return flag

    async def is_temporary(self) -> bool:
        url = self._base_url.join("duoduo-service/transfer/environment/temp/target")
        params = self._common_parameters()
        params.update({"time": self._now_time})
        raw_info = await self._get(url, params)
        flag = False
        if raw_info.get("flag") == 1:
            flag = True

        return flag

    async def get_feedback(self, wechat_time: str) -> dict:
        url = self._base_url.join("duoduo-service/transfer/environment/feedbackCount")
        params = self._common_parameters()
        params.update({"time": wechat_time})
        raw_info = await self._get(url, params)

        meaning_dict = {
            "Id1": "a little cold",
            "Id2": "so cold",
            "Id3": "a little hot",
            "Id4": "so hot",
            "Id5": "noisy or blowy",
            "Id6": "so stuffy",
            "Id7": "more sunshine",
            "Id8": "less sunshine",
            "Id9": "send a repairman",
            "Id10": "switch off",
            "Id11": "nice",
            "Id12": "switch on",
        }

        feedback_dic = {
            meaning_dict.get(k): v for k, v in raw_info.items() if k != "result"
        }

        return feedback_dic

    async def get_custom_target(self) -> dict[str, pd.DataFrame]:
        url = self._base_url.join(
            "duoduo-service/transfer/environment/normalAndPreDayTarget"
        )
        params = self._common_parameters()
        params.update(
            {"date": arrow.get(self._now_time, TIME_FMT).date().strftime("%Y%m%d")}
        )
        raw_info = await self._get(url, params)

        try:
            pre_target_df = pd.DataFrame(raw_info.get("preTargets"))
            pre_target_df.set_index("time", inplace=True)
        except (KeyError, TypeError):
            pre_target_df = pd.DataFrame()

        try:
            normal_target_df = pd.DataFrame(raw_info.get("normalTargets"))
            normal_target_df.set_index("time", inplace=True)
        except (KeyError, TypeError):
            normal_target_df = pd.DataFrame()

        return {"pre_targets": pre_target_df, "normal_targets": normal_target_df}

    async def get_current_temperature_target(self) -> float:
        targets = await self.get_custom_target()
        if len(targets.get("pre_targets")) > 0:
            current_targets = targets.get("pre_targets").append(
                targets.get("normal_targets")
            )
        else:
            current_targets = targets.get("normal_targets")
        temp = (
                arrow.get(self._now_time, TIME_FMT).shift(minutes=15).timestamp()
                // (15 * 60)
                * (15 * 60)
        )
        next_quarter_minutes = arrow.get(temp).time().strftime("%H%M%S")
        try:
            current_lower_target = current_targets["temperatureMin"].loc[
                next_quarter_minutes
            ]
            current_upper_target = current_targets["temperatureMax"].loc[
                next_quarter_minutes
            ]
        except KeyError:
            current_lower_target, current_upper_target = np.NAN, np.NAN

        return round_half_up((current_lower_target + current_upper_target) / 2, 2)

    async def env_database_set(self, form: str, value: float) -> None:
        url = self._base_url.join("duoduo-service/transfer/environment/hispoint/set")
        params = self._common_parameters()
        time_str = arrow.get(
            arrow.get(self._now_time, TIME_FMT).timestamp() // 900 * 900
        ).strftime("%Y%m%d%H%M%S")
        params.update({"time": time_str, "type": form, "value": value})
        await self._get(url, params)

    async def env_database_get(self) -> dict[str, pd.DataFrame]:
        url = self._base_url.join("duoduo-service/transfer/environment/hispoint/get")
        params = self._common_parameters()
        params.update(
            {"date": arrow.get(self._now_time, TIME_FMT).date().strftime("%Y%m%d")}
        )
        raw_info = await self._get(url, params)

        result = {}
        if raw_info.get("result") == "success":
            for k, v in raw_info.items():
                if k != "result":
                    if len(v) > 0:
                        temp = {}
                        data = np.array(v)
                        temp.update({"timestamp": data[:, 0]})
                        temp.update({"value": data[:, 1].astype(np.float)})
                        result.update({k: pd.DataFrame(temp)})
                    else:
                        result.update({k: pd.DataFrame()})

        return result

    def set_custom_target(
            self, form: str, target_value: dict[str, list[float]], flag: str = "1"
    ) -> None:
        url = self._base_url.join("duoduo-service/transfer/environment/target/setting")
        params = {
            "projectId": self._project_id,
            "spaceId": self._space_id,
            "timepoint": self._now_time,
            "type": form,
            "flag": flag,
        }
        httpx.post(url, params=params, json=target_value)
        # await self._post(url, params=params, payload=target_value)

    async def set_temporary_custom(self) -> None:
        url = self._base_url.join("duoduo-service/transfer/environment/setServiceFlag")
        params = self._common_parameters()
        params.update({"time": self._now_time})
        await self._get(url, params)

    async def get_equipment(self) -> list[dict]:
        url = self._base_url.join(
            "duoduo-service/object-service/object/equipment/findForServe"
        )
        params = self._common_parameters()
        raw_info = await self._post(url, params)

        result = []
        for eq in raw_info.get("data"):
            result.append({"id": eq.get("id"), "category": eq.get("equipmentCategory")})

        return result


class Duoduo(Service):
    def __init__(self, client: AsyncClient, project_id: str, server_settings=settings):
        super(Duoduo, self).__init__(client)
        self._project_id = project_id
        self._base_url = URL(server_settings.TRANSFER_HOST)
        self._now_time = get_time_str()

    async def get_season(self) -> Season:
        url = self._base_url.join("duoduo-service/transfer/environment/getSeasonType")
        params = {
            "projectId": self._project_id,
            "date": self._now_time,
        }
        raw_info = await self._get(url, params)

        return Season(raw_info.get("data"))

    async def get_fill_count(self) -> dict:
        url = self._base_url.join(
            "duoduo-service/review-service/space/report/quarter/query"
        )
        payload = {
            "criteria": {
                "projectId": self._project_id,
                "date": arrow.get(self._now_time, TIME_FMT).date().strftime("%Y%m%d"),
            },
            "orders": [{"column": "time", "asc": False}],
            "page": 1,
            "size": 1,
        }

        raw_info = await self._post(url, payload=payload)

        try:
            result = raw_info.get("content")[-1]
        except (IndexError, TypeError):
            result = {}

        return result

    async def get_space_by_equipment(self, equipment_id: str) -> list[dict]:
        url = self._base_url.join(
            "duoduo-service/object-service/object/space/findForServe"
        )
        params = {"projectId": self._project_id, "objectId": equipment_id}
        raw_info = await self._post(url, params)

        result = []
        for sp in raw_info.get("data"):
            if sp.get("isControlled"):
                result.append({"id": sp.get("id")})

        return result

    async def get_system_by_equipment(self, equipment_id: str) -> list:
        url = self._base_url.join(
            "duoduo-service/object-service/object/system/findForCompose"
        )
        params = {"projectId": self._project_id, "equipmentId": equipment_id}
        raw_info = await self._post(url, params)

        system_list = []
        for sy in raw_info.get("data"):
            system_list.append({"id": sy.get("id")})

        return system_list

    async def get_day_type(self) -> dict:
        url = self._base_url.join("duoduo-service/custom-service/custom/getDateInfo")
        params = {
            "projectId": self._project_id,
            "date": arrow.get(self._now_time, TIME_FMT).date().strftime("%Y%m%d"),
        }
        raw_info = await self._get(url, params)

        result = {
            "day_type": raw_info.get("dayType"),
            "season": raw_info.get("seasonType"),
        }

        return result

    async def query_device_virtual_data(self, device_id: str, info_code: str) -> float:
        url = self._base_url.join("duoduo-service/review-service/equipment/order/query")
        payload = {
            "criteria": {
                "projectId": self._project_id,
                "objectId": device_id,
                "date": arrow.get(self._now_time, TIME_FMT).date().strftime("%Y%m%d"),
                "funcId": info_code,
            }
        }
        raw_info = await self._post(url, payload=payload)

        try:
            latest_data = raw_info.get("data")[-1].get("value")
            latest_time = raw_info.get("data")[-1].get("realTime")
            if arrow.get(latest_time, TIME_FMT).shift(minutes=15) < arrow.get(
                    self._now_time, TIME_FMT
            ):
                value = np.NAN
            else:
                value = latest_data
        except (KeyError, TypeError, IndexError):
            value = np.NAN

        return value

    async def query_fill_rate_by_device(self, device_id: str) -> [float, float]:
        url = self._base_url.join(
            "duoduo-service/review-service/space/quarter/getQueryByCategory"
        )
        payload = {
            "criteria": {
                "projectId": self._project_id,
                "date": arrow.get(self._now_time, TIME_FMT).date().strftime("%Y%m%d"),
                "eqId": device_id,
            }
        }

        raw_info = await self._post(url, payload=payload)

        try:
            value_info = raw_info["content"][-1]
            hot_count = value_info["hotSpaceNum"]
            cold_count = value_info["coldSpaceNum"]
            total = value_info["spaceNum"]

            hot_rate = hot_count / total
            cold_rate = cold_count / total
        except (KeyError, IndexError, ZeroDivisionError):
            hot_rate, cold_rate = np.NAN, np.NAN

        return hot_rate, cold_rate