"""Technical indicator tools.

These tools allow the agent to:
1. Discover available indicators (list, search, get info)
2. Add indicators to the chart
3. Update/remove indicators
4. Query currently applied indicators
"""

from typing import Dict, Any, List, Optional
from langchain_core.tools import tool
import logging
import time

logger = logging.getLogger(__name__)


def _get_indicator_registry():
    """Get the global indicator registry instance."""
    from . import _indicator_registry
    return _indicator_registry


def _get_registry():
    """Get the global sync registry instance."""
    from . import _registry
    return _registry


def _get_indicator_store():
    """Get the global IndicatorStore instance."""
    registry = _get_registry()
    if registry and "IndicatorStore" in registry.entries:
        return registry.entries["IndicatorStore"].model
    return None


@tool
def list_indicators() -> List[str]:
    """List all available technical indicators.

    Returns:
        List of indicator names that can be used in analysis and strategies
    """
    registry = _get_indicator_registry()
    if not registry:
        return []
    return registry.list_indicators()


@tool
def get_indicator_info(indicator_name: str) -> Dict[str, Any]:
    """Get detailed information about a specific indicator.

    Retrieves metadata including description, parameters, category, use cases,
    input/output schemas, and references.

    Args:
        indicator_name: Name of the indicator (e.g., "RSI", "SMA", "MACD")

    Returns:
        Dictionary containing:
        - name: Indicator name
        - display_name: Human-readable name
        - description: What the indicator computes and why it's useful
        - category: Category (momentum, trend, volatility, volume, etc.)
        - parameters: List of configurable parameters with types and defaults
        - use_cases: Common trading scenarios where this indicator helps
        - tags: Searchable tags
        - input_schema: Required input columns (e.g., OHLCV requirements)
        - output_schema: Columns this indicator produces

    Raises:
        ValueError: If indicator_name is not found
    """
    registry = _get_indicator_registry()
    if not registry:
        raise ValueError("IndicatorRegistry not initialized")

    metadata = registry.get_metadata(indicator_name)
    if not metadata:
        total_count = len(registry.list_indicators())
        raise ValueError(
            f"Indicator '{indicator_name}' not found. "
            f"Total available: {total_count} indicators. "
            f"Use search_indicators() to find indicators by name, category, or tag."
        )

    input_schema = registry.get_input_schema(indicator_name)
    output_schema = registry.get_output_schema(indicator_name)

    result = metadata.model_dump()
    result["input_schema"] = input_schema.model_dump() if input_schema else None
    result["output_schema"] = output_schema.model_dump() if output_schema else None

    return result


@tool
def search_indicators(
    query: Optional[str] = None,
    category: Optional[str] = None,
    tag: Optional[str] = None
) -> List[Dict[str, Any]]:
    """Search for indicators by text query, category, or tag.

    Returns lightweight summaries - use get_indicator_info() for full details on specific indicators.

    Use this to discover relevant indicators for your trading strategy or analysis.
    Can filter by category (momentum, trend, volatility, etc.) or search by keywords.

    Args:
        query: Optional text search across names, descriptions, and use cases
        category: Optional category filter (momentum, trend, volatility, volume, pattern, etc.)
        tag: Optional tag filter (e.g., "oscillator", "moving-average", "talib")

    Returns:
        List of lightweight indicator summaries. Each contains:
        - name: Indicator name (use with get_indicator_info() for full details)
        - display_name: Human-readable name
        - description: Brief one-line description
        - category: Category (momentum, trend, volatility, etc.)

    Example:
        # Find all momentum indicators
        results = search_indicators(category="momentum")
        # Returns [{name: "RSI", display_name: "RSI", description: "...", category: "momentum"}, ...]

        # Then get details on interesting ones
        rsi_details = get_indicator_info("RSI")  # Full parameters, schemas, use cases

        # Search for moving average indicators
        search_indicators(query="moving average")

        # Find all TA-Lib indicators
        search_indicators(tag="talib")
    """
    registry = _get_indicator_registry()
    if not registry:
        raise ValueError("IndicatorRegistry not initialized")

    results = []

    if query:
        results = registry.search_by_text(query)
    elif category:
        results = registry.search_by_category(category)
    elif tag:
        results = registry.search_by_tag(tag)
    else:
        # Return all indicators if no filter
        results = registry.get_all_metadata()

    # Return lightweight summaries only
    return [
        {
            "name": r.name,
            "display_name": r.display_name,
            "description": r.description,
            "category": r.category
        }
        for r in results
    ]


@tool
def get_indicator_categories() -> Dict[str, int]:
    """Get all indicator categories and their counts.

    Returns a summary of available indicator categories, useful for
    exploring what types of indicators are available.

    Returns:
        Dictionary mapping category name to count of indicators in that category.
        Example: {"momentum": 25, "trend": 15, "volatility": 8, ...}
    """
    registry = _get_indicator_registry()
    if not registry:
        raise ValueError("IndicatorRegistry not initialized")

    categories: Dict[str, int] = {}
    for metadata in registry.get_all_metadata():
        category = metadata.category
        categories[category] = categories.get(category, 0) + 1

    return categories


@tool
async def add_indicator_to_chart(
    indicator_id: str,
    talib_name: str,
    parameters: Optional[Dict[str, Any]] = None,
    symbol: Optional[str] = None
) -> Dict[str, Any]:
    """Add a technical indicator to the chart.

    This will create a new indicator instance and display it on the TradingView chart.
    The indicator will be synchronized with the frontend in real-time.

    Args:
        indicator_id: Unique identifier for this indicator instance (e.g., 'rsi_14', 'sma_50')
        talib_name: Name of the TA-Lib indicator (e.g., 'RSI', 'SMA', 'MACD', 'BBANDS')
                   Use search_indicators() or get_indicator_info() to find available indicators
        parameters: Optional dictionary of indicator parameters
                   Example for RSI: {'timeperiod': 14}
                   Example for SMA: {'timeperiod': 50}
                   Example for MACD: {'fastperiod': 12, 'slowperiod': 26, 'signalperiod': 9}
                   Example for BBANDS: {'timeperiod': 20, 'nbdevup': 2, 'nbdevdn': 2}
        symbol: Optional symbol to apply the indicator to (defaults to current chart symbol)

    Returns:
        Dictionary with:
        - status: 'created' or 'updated'
        - indicator: The complete indicator object

    Example:
        # Add RSI(14)
        await add_indicator_to_chart(
            indicator_id='rsi_14',
            talib_name='RSI',
            parameters={'timeperiod': 14}
        )

        # Add 50-period SMA
        await add_indicator_to_chart(
            indicator_id='sma_50',
            talib_name='SMA',
            parameters={'timeperiod': 50}
        )

        # Add MACD with default parameters
        await add_indicator_to_chart(
            indicator_id='macd_default',
            talib_name='MACD'
        )
    """
    from schema.indicator import IndicatorInstance

    registry = _get_registry()
    if not registry:
        raise ValueError("SyncRegistry not initialized")

    indicator_store = _get_indicator_store()
    if not indicator_store:
        raise ValueError("IndicatorStore not initialized")

    # Verify the indicator exists
    indicator_registry = _get_indicator_registry()
    if not indicator_registry:
        raise ValueError("IndicatorRegistry not initialized")

    metadata = indicator_registry.get_metadata(talib_name)
    if not metadata:
        raise ValueError(
            f"Indicator '{talib_name}' not found. "
            f"Use search_indicators() to find available indicators."
        )

    # Check if updating existing indicator
    existing_indicator = indicator_store.indicators.get(indicator_id)
    is_update = existing_indicator is not None

    # If symbol is not provided, try to get it from ChartStore
    if symbol is None and "ChartStore" in registry.entries:
        chart_store = registry.entries["ChartStore"].model
        if hasattr(chart_store, 'chart_state') and hasattr(chart_store.chart_state, 'symbol'):
            symbol = chart_store.chart_state.symbol
            logger.info(f"Using current chart symbol for indicator: {symbol}")

    now = int(time.time())

    # Create indicator instance
    indicator = IndicatorInstance(
        id=indicator_id,
        talib_name=talib_name,
        instance_name=f"{talib_name}_{indicator_id}",
        parameters=parameters or {},
        visible=True,
        pane='chart',  # Most indicators go on the chart pane
        symbol=symbol,
        created_at=existing_indicator.get('created_at') if existing_indicator else now,
        modified_at=now
    )

    # Update the store
    indicator_store.indicators[indicator_id] = indicator.model_dump(mode="json")

    # Trigger sync
    await registry.push_all()

    logger.info(
        f"{'Updated' if is_update else 'Created'} indicator '{indicator_id}' "
        f"(TA-Lib: {talib_name}) with parameters: {parameters}"
    )

    return {
        "status": "updated" if is_update else "created",
        "indicator": indicator.model_dump(mode="json")
    }


@tool
async def remove_indicator_from_chart(indicator_id: str) -> Dict[str, str]:
    """Remove an indicator from the chart.

    Args:
        indicator_id: ID of the indicator instance to remove

    Returns:
        Dictionary with status message

    Raises:
        ValueError: If indicator doesn't exist

    Example:
        await remove_indicator_from_chart('rsi_14')
    """
    registry = _get_registry()
    if not registry:
        raise ValueError("SyncRegistry not initialized")

    indicator_store = _get_indicator_store()
    if not indicator_store:
        raise ValueError("IndicatorStore not initialized")

    if indicator_id not in indicator_store.indicators:
        raise ValueError(f"Indicator '{indicator_id}' not found")

    # Delete the indicator
    del indicator_store.indicators[indicator_id]

    # Trigger sync
    await registry.push_all()

    logger.info(f"Removed indicator '{indicator_id}'")

    return {
        "status": "success",
        "message": f"Indicator '{indicator_id}' removed"
    }


@tool
def list_chart_indicators(symbol: Optional[str] = None) -> List[Dict[str, Any]]:
    """List all indicators currently applied to the chart.

    Args:
        symbol: Optional filter by symbol (defaults to current chart symbol)

    Returns:
        List of indicator instances, each containing:
        - id: Indicator instance ID
        - talib_name: TA-Lib indicator name
        - instance_name: Display name
        - parameters: Current parameter values
        - visible: Whether indicator is visible
        - pane: Which pane it's displayed in
        - symbol: Symbol it's applied to

    Example:
        # List all indicators on current symbol
        indicators = list_chart_indicators()

        # List indicators on specific symbol
        btc_indicators = list_chart_indicators(symbol='BINANCE:BTC/USDT')
    """
    indicator_store = _get_indicator_store()
    if not indicator_store:
        raise ValueError("IndicatorStore not initialized")

    logger.info(f"list_chart_indicators: Raw store indicators: {indicator_store.indicators}")

    # If symbol is not provided, try to get it from ChartStore
    if symbol is None:
        registry = _get_registry()
        if registry and "ChartStore" in registry.entries:
            chart_store = registry.entries["ChartStore"].model
            if hasattr(chart_store, 'chart_state') and hasattr(chart_store.chart_state, 'symbol'):
                symbol = chart_store.chart_state.symbol

    indicators = list(indicator_store.indicators.values())

    logger.info(f"list_chart_indicators: Converted to list: {indicators}")
    logger.info(f"list_chart_indicators: Filtering by symbol: {symbol}")

    # Filter by symbol if provided
    if symbol:
        indicators = [ind for ind in indicators if ind.get('symbol') == symbol]

    logger.info(f"list_chart_indicators: Returning {len(indicators)} indicators")
    return indicators


@tool
def get_chart_indicator(indicator_id: str) -> Dict[str, Any]:
    """Get details of a specific indicator on the chart.

    Args:
        indicator_id: ID of the indicator instance

    Returns:
        Dictionary containing the indicator data

    Raises:
        ValueError: If indicator doesn't exist

    Example:
        indicator = get_chart_indicator('rsi_14')
        print(f"Indicator: {indicator['talib_name']}")
        print(f"Parameters: {indicator['parameters']}")
    """
    indicator_store = _get_indicator_store()
    if not indicator_store:
        raise ValueError("IndicatorStore not initialized")

    indicator = indicator_store.indicators.get(indicator_id)
    if not indicator:
        raise ValueError(f"Indicator '{indicator_id}' not found")

    return indicator


INDICATOR_TOOLS = [
    # Discovery tools
    list_indicators,
    get_indicator_info,
    search_indicators,
    get_indicator_categories,
    # Chart indicator management tools
    add_indicator_to_chart,
    remove_indicator_from_chart,
    list_chart_indicators,
    get_chart_indicator
]
