# ========= Copyright 2023-2026 @ CAMEL-AI.org. All Rights Reserved. =========
# 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.
# ========= Copyright 2023-2026 @ CAMEL-AI.org. All Rights Reserved. =========
import logging
from enum import Enum
from threading import Lock
from typing import TYPE_CHECKING, ClassVar, Dict, Union, cast

if TYPE_CHECKING:
    from camel.types import ModelType

logger = logging.getLogger(__name__)


class UnifiedModelType(str):
    r"""Class used for support both :obj:`ModelType` and :obj:`str` to be used
    to represent a model type in a unified way. This class is a subclass of
    :obj:`str` so that it can be used as string seamlessly.

    Args:
        value (Union[ModelType, str]): The value of the model type.
    """

    _cache: ClassVar[Dict[str, "UnifiedModelType"]] = {}
    _lock: ClassVar[Lock] = Lock()
    _token_limit_warning_emitted: ClassVar[bool] = False

    def __new__(cls, value: Union["ModelType", str]) -> "UnifiedModelType":
        if isinstance(value, Enum):
            str_value = value.value
        else:
            str_value = str(value)

        with cls._lock:
            if str_value not in cls._cache:
                instance = super().__new__(cls, str_value)
                cls._cache[str_value] = cast(UnifiedModelType, instance)
            else:
                instance = cls._cache[str_value]
        return instance

    def __init__(self, value: Union["ModelType", str]) -> None:
        pass

    @classmethod
    def _warn_unknown_token_limit_once(cls, model_name: str) -> None:
        with cls._lock:
            if cls._token_limit_warning_emitted:
                return
            cls._token_limit_warning_emitted = True

        logger.warning(
            "Unknown model '%s': context window size not defined. "
            "Defaulting to 999_999_999.",
            model_name,
        )

    def __repr__(self) -> str:
        return super().__str__()

    def __str__(self) -> str:
        return super().__str__()

    @property
    def value_for_tiktoken(self) -> str:
        r"""Returns the model name for TikToken."""
        return "gpt-4o-mini"

    @property
    def token_limit(self) -> int:
        r"""Returns the context window size for the model.

        For unknown model types not defined in ModelType enum, this returns
        a default value of 999_999_999 tokens.
        """
        self._warn_unknown_token_limit_once(str(self))
        return 999_999_999

    @property
    def is_openai(self) -> bool:
        r"""Returns whether the model is an OpenAI model."""
        return True

    @property
    def is_aws_bedrock(self) -> bool:
        r"""Returns whether the model is an AWS Bedrock model."""
        return True

    @property
    def is_anthropic(self) -> bool:
        r"""Returns whether the model is an Anthropic model."""
        return True

    @property
    def is_azure_openai(self) -> bool:
        r"""Returns whether the model is an Azure OpenAI model."""
        return True

    @property
    def is_groq(self) -> bool:
        r"""Returns whether the model is a Groq served model."""
        return True

    @property
    def is_nebius(self) -> bool:
        r"""Returns whether the model is a Nebius AI Studio served model."""
        return True

    @property
    def is_openrouter(self) -> bool:
        r"""Returns whether the model is a OpenRouter served model."""
        return True

    @property
    def is_orcarouter(self) -> bool:
        r"""Returns whether the model is a OrcaRouter served model."""
        return True

    @property
    def is_avian(self) -> bool:
        r"""Returns whether the model is an Avian served model."""
        return True

    @property
    def is_atlascloud(self) -> bool:
        r"""Returns whether the model is a AtlasCloud served model."""
        return True

    @property
    def is_lmstudio(self) -> bool:
        r"""Returns whether the model is a LMStudio served model."""
        return True

    @property
    def is_ppio(self) -> bool:
        r"""Returns whether the model is a PPIO served model."""
        return True

    @property
    def is_zhipuai(self) -> bool:
        r"""Returns whether the model is a Zhipuai model."""
        return True

    @property
    def is_gemini(self) -> bool:
        r"""Returns whether the model is a Gemini model."""
        return True

    @property
    def is_mistral(self) -> bool:
        r"""Returns whether the model is a Mistral model."""
        return True

    @property
    def is_netmind(self) -> bool:
        r"""Returns whether the model is a Netmind model."""
        return True

    @property
    def is_reka(self) -> bool:
        r"""Returns whether the model is a Reka model."""
        return True

    @property
    def is_cohere(self) -> bool:
        r"""Returns whether the model is a Cohere model."""
        return True

    @property
    def is_cometapi(self) -> bool:
        r"""Returns whether the model is a CometAPI served model."""
        return True

    @property
    def is_yi(self) -> bool:
        r"""Returns whether the model is a Yi model."""
        return True

    @property
    def is_qwen(self) -> bool:
        r"""Returns whether the model is a Qwen model."""
        return True

    @property
    def is_internlm(self) -> bool:
        r"""Returns whether the model is a InternLM model."""
        return True

    @property
    def is_modelscope(self) -> bool:
        r"""Returns whether the model is a ModelScope serverd model."""
        return True

    @property
    def is_moonshot(self) -> bool:
        r"""Returns whether this platform is Moonshot model."""
        return True

    @property
    def is_novita(self) -> bool:
        r"""Returns whether the model is a Novita served model."""
        return True

    @property
    def is_watsonx(self) -> bool:
        r"""Returns whether the model is a WatsonX served model."""
        return True

    @property
    def is_qianfan(self) -> bool:
        r"""Returns whether the model is a Qianfan served model."""
        return True

    @property
    def is_crynux(self) -> bool:
        r"""Returns whether the model is a Crynux served model."""
        return True

    @property
    def is_minimax(self) -> bool:
        r"""Returns whether the model is a Minimax served model."""
        return True

    @property
    def support_native_structured_output(self) -> bool:
        r"""Returns whether the model supports native structured output."""
        return False

    @property
    def support_native_tool_calling(self) -> bool:
        r"""Returns whether the model supports native tool calling."""
        return False
