"""Test ChatAnthropic chat model."""

from enum import Enum
from typing import List, Optional

from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel, Field

from langchain_anthropic.experimental import ChatAnthropicTools

MODEL_NAME = "claude-3-sonnet-20240229"
BIG_MODEL_NAME = "claude-3-opus-20240229"

#####################################
### Test Basic features, no tools ###
#####################################


def test_stream() -> None:
    """Test streaming tokens from Anthropic."""
    llm = ChatAnthropicTools(model_name=MODEL_NAME)  # type: ignore[call-arg, call-arg]

    for token in llm.stream("I'm Pickle Rick"):
        assert isinstance(token.content, str)


async def test_astream() -> None:
    """Test streaming tokens from Anthropic."""
    llm = ChatAnthropicTools(model_name=MODEL_NAME)  # type: ignore[call-arg, call-arg]

    async for token in llm.astream("I'm Pickle Rick"):
        assert isinstance(token.content, str)


async def test_abatch() -> None:
    """Test streaming tokens from ChatAnthropicTools."""
    llm = ChatAnthropicTools(model_name=MODEL_NAME)  # type: ignore[call-arg, call-arg]

    result = await llm.abatch(["I'm Pickle Rick", "I'm not Pickle Rick"])
    for token in result:
        assert isinstance(token.content, str)


async def test_abatch_tags() -> None:
    """Test batch tokens from ChatAnthropicTools."""
    llm = ChatAnthropicTools(model_name=MODEL_NAME)  # type: ignore[call-arg, call-arg]

    result = await llm.abatch(
        ["I'm Pickle Rick", "I'm not Pickle Rick"], config={"tags": ["foo"]}
    )
    for token in result:
        assert isinstance(token.content, str)


def test_batch() -> None:
    """Test batch tokens from ChatAnthropicTools."""
    llm = ChatAnthropicTools(model_name=MODEL_NAME)  # type: ignore[call-arg, call-arg]

    result = llm.batch(["I'm Pickle Rick", "I'm not Pickle Rick"])
    for token in result:
        assert isinstance(token.content, str)


async def test_ainvoke() -> None:
    """Test invoke tokens from ChatAnthropicTools."""
    llm = ChatAnthropicTools(model_name=MODEL_NAME)  # type: ignore[call-arg, call-arg]

    result = await llm.ainvoke("I'm Pickle Rick", config={"tags": ["foo"]})
    assert isinstance(result.content, str)


def test_invoke() -> None:
    """Test invoke tokens from ChatAnthropicTools."""
    llm = ChatAnthropicTools(model_name=MODEL_NAME)  # type: ignore[call-arg, call-arg]

    result = llm.invoke("I'm Pickle Rick", config=dict(tags=["foo"]))
    assert isinstance(result.content, str)


def test_system_invoke() -> None:
    """Test invoke tokens with a system message"""
    llm = ChatAnthropicTools(model_name=MODEL_NAME)  # type: ignore[call-arg, call-arg]

    prompt = ChatPromptTemplate.from_messages(
        [
            (
                "system",
                "You are an expert cartographer. If asked, you are a cartographer. "
                "STAY IN CHARACTER",
            ),
            ("human", "Are you a mathematician?"),
        ]
    )

    chain = prompt | llm

    result = chain.invoke({})
    assert isinstance(result.content, str)


##################
### Test Tools ###
##################


def test_with_structured_output() -> None:
    class Person(BaseModel):
        name: str
        age: int

    chain = ChatAnthropicTools(  # type: ignore[call-arg, call-arg]
        model_name=BIG_MODEL_NAME,
        temperature=0,
        default_headers={"anthropic-beta": "tools-2024-04-04"},
    ).with_structured_output(Person)
    result = chain.invoke("Erick is 27 years old")
    assert isinstance(result, Person)
    assert result.name == "Erick"
    assert result.age == 27


def test_anthropic_complex_structured_output() -> None:
    class ToneEnum(str, Enum):
        positive = "positive"
        negative = "negative"

    class Email(BaseModel):
        """Relevant information about an email."""

        sender: Optional[str] = Field(
            None, description="The sender's name, if available"
        )
        sender_phone_number: Optional[str] = Field(
            None, description="The sender's phone number, if available"
        )
        sender_address: Optional[str] = Field(
            None, description="The sender's address, if available"
        )
        action_items: List[str] = Field(
            ..., description="A list of action items requested by the email"
        )
        topic: str = Field(
            ..., description="High level description of what the email is about"
        )
        tone: ToneEnum = Field(..., description="The tone of the email.")

    prompt = ChatPromptTemplate.from_messages(
        [
            (
                "human",
                "What can you tell me about the following email? Make sure to answer in the correct format: {email}",  # noqa: E501
            ),
        ]
    )

    llm = ChatAnthropicTools(  # type: ignore[call-arg, call-arg]
        temperature=0,
        model_name=BIG_MODEL_NAME,
        default_headers={"anthropic-beta": "tools-2024-04-04"},
    )

    extraction_chain = prompt | llm.with_structured_output(Email)

    response = extraction_chain.invoke(
        {
            "email": "From: Erick. The email is about the new project. The tone is positive. The action items are to send the report and to schedule a meeting."  # noqa: E501
        }
    )
    assert isinstance(response, Email)
