# ------------------------------------------------------------------------------
#  Copyright 2022 Upstream Data Inc                                            -
#                                                                              -
#  Licensed under the Apache License, Version 2.0 (the "License");             -
#  you may not use this file except in compliance with the License.            -
#  You may obtain a copy of the License at                                     -
#                                                                              -
#      http://www.apache.org/licenses/LICENSE-2.0                              -
#                                                                              -
#  Unless required by applicable law or agreed to in writing, software         -
#  distributed under the License is distributed on an "AS IS" BASIS,           -
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.    -
#  See the License for the specific language governing permissions and         -
#  limitations under the License.                                              -
# ------------------------------------------------------------------------------

from typing import Any

from pydantic import BaseModel, Field

from pyasic.config.fans import (
    FanModeConfig,
    FanModeImmersion,
    FanModeManual,
    FanModeNormal,
)
from pyasic.config.frequency import FreqLevel
from pyasic.config.mining import (
    MiningModeConfig,
    MiningModeHashrateTune,
    MiningModeHPM,
    MiningModeLPM,
    MiningModeManual,
    MiningModeNormal,
    MiningModePowerTune,
    MiningModePreset,
    MiningModeSleep,
)

# Type aliases for config field types
FanModeType = FanModeNormal | FanModeManual | FanModeImmersion | FanModeConfig
MiningModeType = (
    MiningModeNormal
    | MiningModeHPM
    | MiningModeLPM
    | MiningModeSleep
    | MiningModeManual
    | MiningModePowerTune
    | MiningModeHashrateTune
    | MiningModePreset
    | MiningModeConfig
)
from pyasic.config.mining.scaling import ScalingConfig
from pyasic.config.pools import PoolConfig
from pyasic.config.temperature import TemperatureConfig
from pyasic.misc import merge_dicts


class MinerConfig(BaseModel):
    """Represents the configuration for a miner including pool configuration,
    fan mode, temperature settings, mining mode, and power scaling."""

    class Config:
        arbitrary_types_allowed = True

    pools: PoolConfig = Field(default_factory=PoolConfig.default)
    fan_mode: FanModeType = Field(default_factory=FanModeConfig.default)
    temperature: TemperatureConfig = Field(default_factory=TemperatureConfig.default)
    mining_mode: MiningModeType = Field(default_factory=MiningModeConfig.default)
    freq_level: FreqLevel = Field(default_factory=FreqLevel.default)

    def __getitem__(self, item: str) -> Any:
        try:
            return getattr(self, item)
        except AttributeError:
            raise KeyError

    def as_dict(self) -> dict:
        """Converts the MinerConfig object to a dictionary."""
        return self.model_dump()

    def as_am_modern(self, user_suffix: str | None = None) -> dict:
        """Generates the configuration in the format suitable for modern Antminers."""
        return {
            **self.fan_mode.as_am_modern(),
            **self.freq_level.as_am_modern(),
            **self.mining_mode.as_am_modern(),
            **self.pools.as_am_modern(user_suffix=user_suffix),
            **self.temperature.as_am_modern(),
        }

    def as_hiveon_modern(self, user_suffix: str | None = None) -> dict:
        """Generates the configuration in the format suitable for modern Hiveon."""
        return {
            **self.fan_mode.as_hiveon_modern(),
            "freq-level": "100",
            **self.mining_mode.as_hiveon_modern(),
            **self.pools.as_hiveon_modern(user_suffix=user_suffix),
            **self.temperature.as_hiveon_modern(),
        }

    def as_elphapex(self, user_suffix: str | None = None) -> dict:
        """Generates the configuration in the format suitable for modern Elphapex."""
        return {
            **self.fan_mode.as_elphapex(),
            "fc-freq-level": "100",
            **self.mining_mode.as_elphapex(),
            **self.pools.as_elphapex(user_suffix=user_suffix),
            **self.temperature.as_elphapex(),
        }

    def as_wm(self, user_suffix: str | None = None) -> dict:
        """Generates the configuration in the format suitable for Whatsminers."""
        return {
            **self.fan_mode.as_wm(),
            **self.mining_mode.as_wm(),
            **self.pools.as_wm(user_suffix=user_suffix),
            **self.temperature.as_wm(),
        }

    def as_btminer_v3(self, user_suffix: str | None = None) -> dict:
        """Generates the configuration in the format suitable for Whatsminers running BTMiner V3."""
        return {
            "set.miner.pools": self.pools.as_btminer_v3(),
            **self.mining_mode.as_btminer_v3(),
        }

    def as_am_old(self, user_suffix: str | None = None) -> dict:
        """Generates the configuration in the format suitable for old versions of Antminers."""
        return {
            **self.fan_mode.as_am_old(),
            **self.mining_mode.as_am_old(),
            **self.pools.as_am_old(user_suffix=user_suffix),
            **self.temperature.as_am_old(),
        }

    def as_goldshell(self, user_suffix: str | None = None) -> dict:
        """Generates the configuration in the format suitable for Goldshell miners."""
        return {
            **self.fan_mode.as_goldshell(),
            **self.mining_mode.as_goldshell(),
            **self.pools.as_goldshell(user_suffix=user_suffix),
            **self.temperature.as_goldshell(),
        }

    def as_avalon(self, user_suffix: str | None = None) -> dict:
        """Generates the configuration in the format suitable for Avalonminers."""
        return {
            **self.fan_mode.as_avalon(),
            **self.mining_mode.as_avalon(),
            **self.pools.as_avalon(user_suffix=user_suffix),
            **self.temperature.as_avalon(),
        }

    def as_inno(self, user_suffix: str | None = None) -> dict:
        """Generates the configuration in the format suitable for Innosilicon miners."""
        return {
            **self.fan_mode.as_inno(),
            **self.mining_mode.as_inno(),
            **self.pools.as_inno(user_suffix=user_suffix),
            **self.temperature.as_inno(),
        }

    def as_bosminer(self, user_suffix: str | None = None) -> dict:
        """Generates the configuration in the bosminer.toml format."""
        return {
            **merge_dicts(self.fan_mode.as_bosminer(), self.temperature.as_bosminer()),
            **self.mining_mode.as_bosminer(),
            **self.pools.as_bosminer(user_suffix=user_suffix),
        }

    def as_boser(self, user_suffix: str | None = None) -> dict:
        """Generates the configuration in the format suitable for BOSer."""
        return {
            **self.fan_mode.as_boser(),
            **self.temperature.as_boser(),
            **self.mining_mode.as_boser(),
            **self.pools.as_boser(user_suffix=user_suffix),
        }

    def as_epic(self, user_suffix: str | None = None) -> dict:
        """Generates the configuration in the format suitable for ePIC miners."""
        return {
            **merge_dicts(self.fan_mode.as_epic(), self.temperature.as_epic()),
            **self.mining_mode.as_epic(),
            **self.pools.as_epic(user_suffix=user_suffix),
        }

    def as_auradine(self, user_suffix: str | None = None) -> dict:
        """Generates the configuration in the format suitable for Auradine miners."""
        return {
            **self.fan_mode.as_auradine(),
            **self.temperature.as_auradine(),
            **self.mining_mode.as_auradine(),
            **self.pools.as_auradine(user_suffix=user_suffix),
        }

    def as_mara(self, user_suffix: str | None = None) -> dict:
        return {
            **self.fan_mode.as_mara(),
            **self.temperature.as_mara(),
            **self.mining_mode.as_mara(),
            **self.pools.as_mara(user_suffix=user_suffix),
        }

    def as_espminer(self, user_suffix: str | None = None) -> dict:
        return {
            **self.fan_mode.as_espminer(),
            **self.temperature.as_espminer(),
            **self.mining_mode.as_espminer(),
            **self.pools.as_espminer(user_suffix=user_suffix),
        }

    def as_luxos(self, user_suffix: str | None = None) -> dict:
        return {
            **self.fan_mode.as_luxos(),
            **self.temperature.as_luxos(),
            **self.mining_mode.as_luxos(),
            **self.pools.as_luxos(user_suffix=user_suffix),
        }

    def as_vnish(self, user_suffix: str | None = None) -> dict:
        main_cfg = {
            "miner": {
                **self.fan_mode.as_vnish(),
                **self.temperature.as_vnish(),
                **self.mining_mode.as_vnish(),
                **self.pools.as_vnish(user_suffix=user_suffix),
            }
        }
        if isinstance(self.fan_mode, FanModeNormal):
            main_cfg["miner"]["cooling"]["mode"]["param"] = self.temperature.target
        return main_cfg

    def as_hammer(self, *args, **kwargs) -> dict:
        return self.as_am_modern(*args, **kwargs)

    @classmethod
    def from_dict(cls, dict_conf: dict) -> "MinerConfig":
        """Constructs a MinerConfig object from a dictionary."""
        return cls(
            pools=PoolConfig.from_dict(dict_conf.get("pools")),
            mining_mode=MiningModeConfig.from_dict(dict_conf.get("mining_mode")),
            fan_mode=FanModeConfig.from_dict(dict_conf.get("fan_mode")),
            temperature=TemperatureConfig.from_dict(dict_conf.get("temperature")),
            freq_level=FreqLevel.from_dict(dict_conf.get("freq_level")),
        )

    @classmethod
    def from_api(cls, api_pools: dict) -> "MinerConfig":
        """Constructs a MinerConfig object from API pool data."""
        return cls(pools=PoolConfig.from_api(api_pools))

    @classmethod
    def from_am_modern(cls, web_conf: dict) -> "MinerConfig":
        """Constructs a MinerConfig object from web configuration for modern Antminers."""
        return cls(
            pools=PoolConfig.from_am_modern(web_conf),
            mining_mode=MiningModeConfig.from_am_modern(web_conf),
            fan_mode=FanModeConfig.from_am_modern(web_conf),
            freq_level=FreqLevel.from_am_modern(web_conf),
        )

    @classmethod
    def from_hiveon_modern(cls, web_conf: dict) -> "MinerConfig":
        """Constructs a MinerConfig object from web configuration for Hiveon."""
        return cls(
            pools=PoolConfig.from_hiveon_modern(web_conf),
            mining_mode=MiningModeConfig.from_hiveon_modern(web_conf),
            fan_mode=FanModeConfig.from_hiveon_modern(web_conf),
        )

    @classmethod
    def from_elphapex(cls, web_conf: dict) -> "MinerConfig":
        """Constructs a MinerConfig object from web configuration for modern Antminers."""
        return cls(
            pools=PoolConfig.from_elphapex(web_conf),
            mining_mode=MiningModeConfig.from_elphapex(web_conf),
            fan_mode=FanModeConfig.from_elphapex(web_conf),
        )

    @classmethod
    def from_am_old(cls, web_conf: dict) -> "MinerConfig":
        """Constructs a MinerConfig object from web configuration for old versions of Antminers."""
        return cls.from_am_modern(web_conf)

    @classmethod
    def from_goldshell(cls, web_conf: dict) -> "MinerConfig":
        """Constructs a MinerConfig object from web configuration for Goldshell miners."""
        return cls(pools=PoolConfig.from_am_modern(web_conf))

    @classmethod
    def from_goldshell_list(cls, web_conf: list) -> "MinerConfig":
        """Constructs a MinerConfig object from web configuration for Goldshell miners."""
        return cls(pools=PoolConfig.from_goldshell(web_conf))

    @classmethod
    def from_goldshell_byte(cls, web_conf: list) -> "MinerConfig":
        """Constructs a MinerConfig object from web configuration for Goldshell Byte miners."""
        return cls(pools=PoolConfig.from_goldshell_byte(web_conf))

    @classmethod
    def from_inno(cls, web_pools: list) -> "MinerConfig":
        """Constructs a MinerConfig object from web configuration for Innosilicon miners."""
        return cls(pools=PoolConfig.from_inno(web_pools))

    @classmethod
    def from_bosminer(cls, toml_conf: dict) -> "MinerConfig":
        """Constructs a MinerConfig object from the bosminer.toml file, same as the `as_bosminer` dumps a dict for writing to that file as toml."""
        return cls(
            pools=PoolConfig.from_bosminer(toml_conf),
            mining_mode=MiningModeConfig.from_bosminer(toml_conf),
            fan_mode=FanModeConfig.from_bosminer(toml_conf),
            temperature=TemperatureConfig.from_bosminer(toml_conf),
        )

    @classmethod
    def from_boser(cls, grpc_miner_conf: dict) -> "MinerConfig":
        """Constructs a MinerConfig object from gRPC configuration for BOSer."""
        return cls(
            pools=PoolConfig.from_boser(grpc_miner_conf),
            mining_mode=MiningModeConfig.from_boser(grpc_miner_conf),
            fan_mode=FanModeConfig.from_boser(grpc_miner_conf),
            temperature=TemperatureConfig.from_boser(grpc_miner_conf),
        )

    @classmethod
    def from_epic(cls, web_conf: dict) -> "MinerConfig":
        """Constructs a MinerConfig object from web configuration for ePIC miners."""
        return cls(
            pools=PoolConfig.from_epic(web_conf),
            fan_mode=FanModeConfig.from_epic(web_conf),
            temperature=TemperatureConfig.from_epic(web_conf),
            mining_mode=MiningModeConfig.from_epic(web_conf),
        )

    @classmethod
    def from_vnish(
        cls, web_settings: dict, web_presets: list[dict], web_perf_summary: dict
    ) -> "MinerConfig":
        """Constructs a MinerConfig object from web settings for VNish miners."""
        return cls(
            pools=PoolConfig.from_vnish(web_settings),
            fan_mode=FanModeConfig.from_vnish(web_settings),
            temperature=TemperatureConfig.from_vnish(web_settings),
            mining_mode=MiningModeConfig.from_vnish(
                web_settings, web_presets, web_perf_summary
            ),
        )

    @classmethod
    def from_auradine(cls, web_conf: dict) -> "MinerConfig":
        """Constructs a MinerConfig object from web configuration for Auradine miners."""
        return cls(
            pools=PoolConfig.from_api(web_conf["pools"]),
            fan_mode=FanModeConfig.from_auradine(web_conf["fan"]),
            mining_mode=MiningModeConfig.from_auradine(web_conf["mode"]),
        )

    @classmethod
    def from_mara(cls, web_miner_config: dict) -> "MinerConfig":
        return cls(
            pools=PoolConfig.from_mara(web_miner_config),
            fan_mode=FanModeConfig.from_mara(web_miner_config),
            mining_mode=MiningModeConfig.from_mara(web_miner_config),
        )

    @classmethod
    def from_espminer(cls, web_system_info: dict) -> "MinerConfig":
        return cls(
            pools=PoolConfig.from_espminer(web_system_info),
            fan_mode=FanModeConfig.from_espminer(web_system_info),
        )

    @classmethod
    def from_iceriver(cls, web_userpanel: dict) -> "MinerConfig":
        return cls(
            pools=PoolConfig.from_iceriver(web_userpanel),
        )

    @classmethod
    def from_luxos(
        cls,
        rpc_tempctrl: dict,
        rpc_fans: dict,
        rpc_pools: dict,
        rpc_groups: dict,
        rpc_config: dict,
        rpc_profiles: dict,
    ) -> "MinerConfig":
        return cls(
            temperature=TemperatureConfig.from_luxos(rpc_tempctrl=rpc_tempctrl),
            fan_mode=FanModeConfig.from_luxos(
                rpc_tempctrl=rpc_tempctrl, rpc_fans=rpc_fans
            ),
            pools=PoolConfig.from_luxos(rpc_pools=rpc_pools, rpc_groups=rpc_groups),
            mining_mode=MiningModeConfig.from_luxos(
                rpc_config=rpc_config, rpc_profiles=rpc_profiles
            ),
        )

    @classmethod
    def from_hammer(cls, *args, **kwargs) -> "MinerConfig":
        return cls.from_am_modern(*args, **kwargs)

    @classmethod
    def from_btminer_v3(
        cls, rpc_pools: dict, rpc_settings: dict, rpc_device_info: dict
    ) -> "MinerConfig":
        return cls(
            pools=PoolConfig.from_btminer_v3(rpc_pools=rpc_pools["msg"]),
            mining_mode=MiningModeConfig.from_btminer_v3(
                rpc_device_info=rpc_device_info, rpc_settings=rpc_settings
            ),
        )
