"""MessageMaker for r.gnode.weather.forecast.sync.req.1_0_0.
Request for forecasted weather information going forwards in time"""

import datetime
import time
import uuid
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
from typing import Tuple

from gridworks.errors import *

import gwatn.types.hack_test_dummy as test_dummy
from gwatn.enums import RecognizedIrradianceType
from gwatn.types.gnode_forecastrequest_ws.r_gnode_weather_forecast_sync_req.r_gnode_weather_forecast_sync_req_1_0_0_payload import (
    Payload,
)
from gwatn.types.hack_type_base import HackTypeBase
from gwatn.types.hack_utils import log_style_utc_date_w_millis


class R_Gnode_Weather_Forecast_Sync_Req_1_0_0(HackTypeBase):
    mp_alias = "r.gnode.weather.forecast.sync.req.1_0_0"
    routing_key_base = "gnode.forecastrequest.ws"

    @classmethod
    def create_payload_from_camel_dict(cls, d: dict) -> Payload:
        if "MpAlias" not in d.keys():
            d["MpAlias"] = "r.gnode.weather.forecast.sync.req.1_0_0"
        if "IrradianceMethodAlias" not in d.keys():
            d["IrradianceMethodAlias"] = None
        if "IrradianceType" not in d.keys():
            d["IrradianceType"] = None
        if "IrradianceSourceAlias" not in d.keys():
            d["IrradianceSourceAlias"] = None
        if "TempMethodAlias" not in d.keys():
            d["TempMethodAlias"] = None
        if "IrlTimeUtc" not in d.keys():
            d["IrlTimeUtc"] = None
        if "TempSourceAlias" not in d.keys():
            d["TempSourceAlias"] = None
        if "SkyClaritySourceAlias" not in d.keys():
            d["SkyClaritySourceAlias"] = None
        if "SkyClarityMethodAlias" not in d.keys():
            d["SkyClarityMethodAlias"] = None
        if "TempUnit" not in d.keys():
            d["TempUnit"] = None
        if "WorldInstanceAlias" not in d.keys():
            d["WorldInstanceAlias"] = None
        p = Payload(
            MpAlias=d["MpAlias"],
            ToGNodeAlias=d["ToGNodeAlias"],
            FromGNodeAlias=d["FromGNodeAlias"],
            StartHourUtc=d["StartHourUtc"],
            IrradianceMethodAlias=d["IrradianceMethodAlias"],
            IncludeIrradiance=d["IncludeIrradiance"],
            IncludeSkyClarity=d["IncludeSkyClarity"],
            IrradianceType=d["IrradianceType"],
            IrradianceSourceAlias=d["IrradianceSourceAlias"],
            TempMethodAlias=d["TempMethodAlias"],
            IrlTimeUtc=d["IrlTimeUtc"],
            IncludeTemp=d["IncludeTemp"],
            TempSourceAlias=d["TempSourceAlias"],
            TotalSlices=d["TotalSlices"],
            UniformSliceDurationHrs=float(d["UniformSliceDurationHrs"]),
            StartYearUtc=d["StartYearUtc"],
            TimezoneString=d["TimezoneString"],
            SkyClaritySourceAlias=d["SkyClaritySourceAlias"],
            SkyClarityMethodAlias=d["SkyClarityMethodAlias"],
            MessageId=d["MessageId"],
            StartDayUtc=d["StartDayUtc"],
            FromGNodeInstanceId=d["FromGNodeInstanceId"],
            LocationAlias=d["LocationAlias"],
            TempUnit=d["TempUnit"],
            StartMonthUtc=d["StartMonthUtc"],
            WorldInstanceAlias=d["WorldInstanceAlias"],
        )
        is_valid, errors = p.is_valid()
        if not is_valid:
            raise SchemaError(errors)
        return p

    @classmethod
    def payload_is_valid(
        cls, payload_as_dict: Dict[str, Any]
    ) -> Tuple[bool, Optional[List[str]]]:
        try:
            p = cls.create_payload_from_camel_dict(payload_as_dict)
        except SchemaError as e:
            errors = [e]
            return False, errors
        return p.is_valid()

    def __init__(
        self,
        agent,
        to_g_node_alias: str,
        include_irradiance: bool,
        include_sky_clarity: bool,
        include_temp: bool,
        total_slices: int,
        uniform_slice_duration_hrs: float,
        start_utc: datetime.datetime,
        timezone_string: str,
        location_alias: str,
        irradiance_type=RecognizedIrradianceType.PlaneOfArray.value,
        irradiance_method_alias: Optional[str] = None,
        irradiance_source_alias: Optional[str] = None,
        temp_method_alias: Optional[str] = None,
        irl_time_utc: Optional[str] = None,
        temp_source_alias: Optional[str] = None,
        sky_clarity_source_alias: Optional[str] = None,
        sky_clarity_method_alias: Optional[str] = None,
        temp_unit: Optional[str] = None,
    ):
        super().__init__(routing_key_base="gnode.forecastrequest.ws", agent=agent)
        self.errors = []
        self.payload = None
        if agent is None:
            raise Exception(
                f"Message protocol {R_Gnode_Weather_Forecast_Sync_Req_1_0_0.mp_alias} must be generated by a message agent"
            )
        if agent == test_dummy.TEST_DUMMY_AGENT:
            from_g_node_alias = test_dummy.TEST_DUMMY_G_NODE_ALIAS
            from_g_node_instance_id = test_dummy.TEST_DUMMY_G_NODE_INSTANCE_ID
            world_instance_alias = test_dummy.TEST_DUMMY_WORLD_INSTANCE_ALIAS
            irl_time_utc = log_style_utc_date_w_millis(time.time())
        else:
            if not agent.gni.g_node.is_g_node:
                raise Exception(
                    f"Message protocol {R_Gnode_Weather_Forecast_Sync_Req_1_0_0.mp_alias} must come from a GNode"
                )
            from_g_node_alias = agent.gni.g_node.alias
            from_g_node_instance_id = agent.gni.g_node_instance_id
            if agent.world_instance.is_simulated:
                world_instance_alias = agent.world_instance.alias
            else:
                world_instance_alias = None
            if agent.is_debug_mode:
                irl_time_utc = log_style_utc_date_w_millis(time.time())
            else:
                irl_time_utc = None
        try:
            uniform_slice_duration_hrs = float(uniform_slice_duration_hrs)
        except ValueError:
            pass  # This will get caught in is_valid() check below

        p = Payload(
            MpAlias=R_Gnode_Weather_Forecast_Sync_Req_1_0_0.mp_alias,
            WorldInstanceAlias=world_instance_alias,
            FromGNodeAlias=from_g_node_alias,
            FromGNodeInstanceId=from_g_node_instance_id,
            ToGNodeAlias=to_g_node_alias,
            StartHourUtc=start_utc.hour,
            IrradianceMethodAlias=irradiance_method_alias,
            IncludeIrradiance=include_irradiance,
            IncludeSkyClarity=include_sky_clarity,
            IrradianceType=irradiance_type,
            IrradianceSourceAlias=irradiance_source_alias,
            TempMethodAlias=temp_method_alias,
            IrlTimeUtc=irl_time_utc,
            IncludeTemp=include_temp,
            TempSourceAlias=temp_source_alias,
            TotalSlices=total_slices,
            UniformSliceDurationHrs=uniform_slice_duration_hrs,
            StartYearUtc=start_utc.year,
            TimezoneString=timezone_string,
            SkyClaritySourceAlias=sky_clarity_source_alias,
            SkyClarityMethodAlias=sky_clarity_method_alias,
            StartDayUtc=start_utc.day,
            LocationAlias=location_alias,
            TempUnit=temp_unit,
            StartMonthUtc=start_utc.month,
            MessageId=str(uuid.uuid4()),
        )

        is_valid, errors = p.is_valid()
        if is_valid is False:
            raise SchemaError(f"Failed to create payload due to these errors: {errors}")
        self.payload = p
