"""Basic mathematical calculation tools."""

import math
from typing import Annotated, Dict, Literal, Union, List
from pydantic import Field
from sympy import sympify, simplify, N
from mcp.types import ToolAnnotations
import numpy as np

from ..server import mcp
from ..core import format_result


@mcp.tool(
    name="calculate",
    description="""Evaluate mathematical expressions using SymPy.

Supports:
    - Arithmetic: +, -, *, /, ^
    - Trigonometry: sin, cos, tan, asin, acos, atan
    - Logarithms: log, ln, exp
    - Constants: pi, e
    - Functions: sqrt, abs

Examples:

SIMPLE ARITHMETIC:
    expression="2 + 2"
    Result: 4

TRIGONOMETRY:
    expression="sin(pi/2)"
    Result: 1.0

WITH VARIABLES:
    expression="x^2 + 2*x + 1", variables={"x": 3}
    Result: 16

MULTIPLE VARIABLES:
    expression="x^2 + y^2", variables={"x": 3, "y": 4}
    Result: 25""",
    annotations=ToolAnnotations(
        title="Expression Calculator",
        readOnlyHint=True,
        idempotentHint=True,
    ),
)
async def calculate(
    expression: Annotated[
        str,
        Field(
            description="Mathematical expression (e.g., '2+2', 'sin(pi/2)', 'x^2+1')", min_length=1
        ),
    ],
    variables: Annotated[
        Dict[str, float] | None,
        Field(description="Variable substitutions (e.g., {'x': 5, 'y': 10})"),
    ] = None,
) -> str:
    """Evaluate mathematical expressions."""
    try:
        expr = sympify(expression)

        if variables:
            result = float(N(expr.subs(variables)))
        else:
            result = float(N(simplify(expr)))

        return format_result(result, {"expression": expression, "variables": variables})
    except Exception as e:
        raise ValueError(
            f"Failed to evaluate expression '{expression}'. "
            f"Error: {str(e)}. "
            f"Example: '2*x + 3' with variables={{'x': 5}}"
        )


@mcp.tool(
    name="percentage",
    description="""Perform percentage calculations: of, increase, decrease, or change.

Examples:

PERCENTAGE OF: 15% of 200
    operation="of", value=200, percentage=15
    Result: 30

INCREASE: 100 increased by 20%
    operation="increase", value=100, percentage=20
    Result: 120

DECREASE: 100 decreased by 20%
    operation="decrease", value=100, percentage=20
    Result: 80

PERCENTAGE CHANGE: from 80 to 100
    operation="change", value=80, percentage=100
    Result: 25 (25% increase)""",
    annotations=ToolAnnotations(
        title="Percentage Calculator",
        readOnlyHint=True,
        idempotentHint=True,
    ),
)
async def percentage(
    operation: Annotated[
        Literal["of", "increase", "decrease", "change"], Field(description="Type of calculation")
    ],
    value: Annotated[float, Field(description="Base value")],
    percentage: Annotated[
        float, Field(description="Percentage amount (or new value for 'change')")
    ],
) -> str:
    """Perform percentage calculations."""
    try:
        if operation == "of":
            result = (percentage / 100) * value
            explanation = f"{percentage}% of {value}"
        elif operation == "increase":
            result = value * (1 + percentage / 100)
            explanation = f"{value} increased by {percentage}%"
        elif operation == "decrease":
            result = value * (1 - percentage / 100)
            explanation = f"{value} decreased by {percentage}%"
        elif operation == "change":
            # percentage is actually the new value in this case
            if value == 0:
                raise ValueError("Cannot calculate percentage change from zero")
            result = ((percentage - value) / value) * 100
            explanation = f"Percentage change from {value} to {percentage}"
        else:
            raise ValueError(f"Unknown operation: {operation}")

        return format_result(
            result,
            {
                "operation": operation,
                "value": value,
                "percentage": percentage,
                "explanation": explanation,
            },
        )
    except Exception as e:
        raise ValueError(f"Percentage calculation failed: {str(e)}")


@mcp.tool(
    name="round",
    description="""Advanced rounding operations with multiple methods.

Methods:
    - round: Round to nearest (3.145 → 3.15 at 2dp)
    - floor: Always round down (3.149 → 3.14)
    - ceil: Always round up (3.141 → 3.15)
    - trunc: Truncate towards zero (-3.7 → -3, 3.7 → 3)

Examples:

ROUND TO NEAREST:
    values=3.14159, method="round", decimals=2
    Result: 3.14

FLOOR (DOWN):
    values=3.14159, method="floor", decimals=2
    Result: 3.14

CEIL (UP):
    values=3.14159, method="ceil", decimals=2
    Result: 3.15

MULTIPLE VALUES:
    values=[3.14159, 2.71828], method="round", decimals=2
    Result: [3.14, 2.72]""",
    annotations=ToolAnnotations(
        title="Advanced Rounding",
        readOnlyHint=True,
        idempotentHint=True,
    ),
)
async def round_values(
    values: Annotated[
        Union[float, List[float]],
        Field(description="Single value or list (e.g., 3.14159 or [3.14, 2.71])"),
    ],
    method: Annotated[
        Literal["round", "floor", "ceil", "trunc"], Field(description="Rounding method")
    ] = "round",
    decimals: Annotated[int, Field(description="Number of decimal places", ge=0)] = 0,
) -> str:
    """Advanced rounding operations."""
    try:
        is_single = isinstance(values, (int, float))
        vals = [values] if is_single else values

        arr = np.array(vals, dtype=float)

        if method == "round":
            result = np.round(arr, decimals)
        elif method == "floor":
            result = np.floor(arr * 10**decimals) / 10**decimals
        elif method == "ceil":
            result = np.ceil(arr * 10**decimals) / 10**decimals
        elif method == "trunc":
            result = np.trunc(arr * 10**decimals) / 10**decimals
        else:
            raise ValueError(f"Unknown method: {method}")

        final_result = float(result[0]) if is_single else result.tolist()

        return format_result(final_result, {"method": method, "decimals": decimals})
    except Exception as e:
        raise ValueError(f"Rounding operation failed: {str(e)}")


@mcp.tool(
    name="convert_units",
    description="""Convert between angle units: degrees ↔ radians.

Examples:

DEGREES TO RADIANS:
    value=180, from_unit="degrees", to_unit="radians"
    Result: 3.14159... (π)

RADIANS TO DEGREES:
    value=3.14159, from_unit="radians", to_unit="degrees"
    Result: 180

RIGHT ANGLE:
    value=90, from_unit="degrees", to_unit="radians"
    Result: 1.5708... (π/2)""",
    annotations=ToolAnnotations(
        title="Unit Converter",
        readOnlyHint=True,
        idempotentHint=True,
    ),
)
async def convert_units(
    value: Annotated[float, Field(description="Value to convert (e.g., 180, 3.14159)")],
    from_unit: Annotated[Literal["degrees", "radians"], Field(description="Source unit")],
    to_unit: Annotated[Literal["degrees", "radians"], Field(description="Target unit")],
) -> str:
    """Convert between angle units."""
    try:
        if from_unit == to_unit:
            result = value
        elif from_unit == "degrees" and to_unit == "radians":
            result = math.radians(value)
        elif from_unit == "radians" and to_unit == "degrees":
            result = math.degrees(value)
        else:
            raise ValueError(f"Unsupported conversion: {from_unit} to {to_unit}")

        return format_result(
            result, {"from_unit": from_unit, "to_unit": to_unit, "original_value": value}
        )
    except Exception as e:
        raise ValueError(f"Unit conversion failed: {str(e)}")
