from typing import Annotated

import structlog
from langchain_core.tools import tool

from src.fx_normalization import get_fx_rate
from src.runtime_services import get_current_market_data_fetcher
from src.ticker_utils import normalize_ticker

logger = structlog.get_logger(__name__)

from src.currency_resolver import resolve_local_trading_currency
from src.ticker_policy import get_ticker_suffix


def _market_data_fetcher():
    """Resolve the active market-data fetcher at call time."""
    return get_current_market_data_fetcher()


@tool
async def calculate_liquidity_metrics(
    ticker: Annotated[str | None, "Stock ticker symbol"] = None,
) -> str:
    """
    Calculate liquidity metrics using the robust MarketDataFetcher.
    Checks 3-month average volume and turnover.
    Handles global currency conversion automatically.
    """
    if not ticker:
        return "Error: No ticker symbol provided."

    normalized_symbol = normalize_ticker(ticker)

    try:
        # Step 1: Fetch ROBUST anchor price (multi-source validated)
        # This prevents "cent-scaling" bugs where history data is 1/100th of reality
        # Anchoring to the validated spot price ensures turnover reflects actual market value.
        anchor_data = await _market_data_fetcher().get_financial_metrics(
            normalized_symbol
        )
        anchor_price = (
            anchor_data.get("currentPrice") if isinstance(anchor_data, dict) else None
        )

        # Step 2: Fetch volume history
        hist = await _market_data_fetcher().get_historical_prices(
            normalized_symbol, period="3mo"
        )

        if hist.empty:
            logger.warning("no_history_found", ticker=ticker)
            return f"""Liquidity Analysis for {ticker}:
Status: FAIL - Insufficient Data
Avg Daily Volume (3mo): N/A
Avg Daily Turnover (USD): N/A
"""

        # Calculate metrics
        avg_volume = hist["Volume"].mean()

        # Use robust anchor price if available; fallback to history mean
        # This ensures liquidity calculation matches the valuation price
        price_for_turnover = (
            anchor_price if anchor_price and anchor_price > 0 else hist["Close"].mean()
        )

        # --- HEARTBEAT CHECK (Trap A: Liquidity Distortion) ---
        # Detect irregular trading patterns that create stale/manipulated pricing
        total_days = len(hist)
        zero_vol_days = (hist["Volume"] == 0).sum()
        flat_days = (hist["Close"] == hist["Close"].shift(1)).sum()
        pct_zero = (zero_vol_days / total_days) * 100 if total_days > 0 else 0
        pct_flat = (flat_days / total_days) * 100 if total_days > 0 else 0

        # Calculate local turnover
        # NOTE: For UK stocks (.L), prices are in Pence, so we must divide by 100
        # to get Pounds before converting to USD.
        if normalized_symbol.endswith(".L"):
            avg_turnover_local = avg_volume * (price_for_turnover / 100.0)
            logger.info("pence_adjustment_applied", ticker=ticker)
        else:
            avg_turnover_local = avg_volume * price_for_turnover

        # Determine currency and FX rate based on resolution
        suffix = get_ticker_suffix(normalized_symbol)
        res = resolve_local_trading_currency(ticker=normalized_symbol)
        if res.code:
            currency = res.code
            resolution_source = res.source
        elif not suffix or suffix == ".US":
            currency = "USD"
            resolution_source = "fallback_us_listing"
        else:
            logger.warning(
                "liquidity_currency_unresolved",
                ticker=ticker,
                resolution_source=res.source,
            )
            return f"""Liquidity Analysis for {ticker}:
Status: ERROR
Error: Could not determine trading currency for turnover conversion.
"""

        # Get FX rate dynamically (with fallback to static rates)
        fx_rate, fx_source = await get_fx_rate(currency, "USD", allow_fallback=True)

        if fx_rate is None:
            # Total FX failure - assume 1.0 and flag as uncertain
            fx_rate = 1.0
            fx_source = "assumed"
            logger.warning(
                "fx_rate_unavailable_using_1.0", ticker=ticker, currency=currency
            )

        logger.info(
            "liquidity_fx_conversion",
            ticker=ticker,
            resolution_source=resolution_source,
            currency=currency,
            fx_rate=fx_rate,
            source=fx_source,
        )

        avg_turnover_usd = avg_turnover_local * fx_rate

        # Thresholds (Aligned with Portfolio Manager Prompt)
        THRESHOLD_PASS = 250_000
        THRESHOLD_MARGINAL = 100_000

        # Failure Conditions
        fails_zero_vol = pct_zero > 15.0
        fails_flat_price = pct_flat > 30.0

        # Determine status with priority: heartbeat issues > insufficient liquidity > marginal > pass
        status = "PASS"
        reasons = []

        if fails_zero_vol or fails_flat_price:
            status = "FAIL (Irregular Trading)"
            if fails_zero_vol:
                reasons.append(f"{int(pct_zero)}% zero-volume days")
            if fails_flat_price:
                reasons.append(f"{int(pct_flat)}% flat-price days")

        elif avg_turnover_usd < THRESHOLD_MARGINAL:
            status = "FAIL (Insufficient Liquidity)"
            reasons.append(
                f"${int(avg_turnover_usd):,} < ${THRESHOLD_MARGINAL:,} minimum"
            )

        elif avg_turnover_usd < THRESHOLD_PASS:
            status = "MARGINAL"
            reasons.append(f"Low liquidity (${int(avg_turnover_usd):,})")

        # else: status remains "PASS"

        # Build status line with reasons if applicable
        status_line = status
        if reasons:
            status_line = f"{status} - {'; '.join(reasons)}"

        agent_note = ""
        if fails_zero_vol or fails_flat_price:
            agent_note = (
                "\nAGENT INSTRUCTION: This is a definitive hard fail based on measured "
                "exchange data. Do not substitute volume, turnover, or liquidity figures "
                "from another listing, a different exchange, or your training knowledge. "
                "Report this ticker as illiquid on this exchange."
            )

        return f"""Liquidity Analysis for {ticker}:
Status: {status_line}
Avg Daily Volume (3mo): {int(avg_volume):,}
Avg Daily Turnover (USD): ${int(avg_turnover_usd):,}
Trading Regularity: {pct_zero:.0f}% zero-volume days, {pct_flat:.0f}% flat-price days (last 3mo)
Details: {currency} turnover converted at FX rate {fx_rate:.6f} (source: {fx_source})
Thresholds: $100,000 USD minimum (MARGINAL), $250,000 USD recommended (PASS), <15% zero-volume days, <30% flat-price days{agent_note}
"""

    except Exception as e:
        logger.error(
            "liquidity_calculation_failed", ticker=ticker, error=str(e), exc_info=True
        )
        return f"""Liquidity Analysis for {ticker}:
Status: ERROR
Error: {str(e)}
"""
