"""
Enhanced news sentiment analysis using Tiingo News API or LLM-based analysis.

This module provides reliable news sentiment analysis by:
1. Using Tiingo's get_news method (if available)
2. Falling back to LLM-based sentiment analysis using existing research tools
3. Never relying on undefined EXTERNAL_DATA_API_KEY
"""

import asyncio
import logging
import os
import uuid
from datetime import datetime, timedelta
from typing import Any

from tiingo import TiingoClient

from maverick_mcp.api.middleware.mcp_logging import get_tool_logger
from maverick_mcp.config.settings import get_settings

logger = logging.getLogger(__name__)
settings = get_settings()


def get_tiingo_client() -> TiingoClient | None:
    """Get or create Tiingo client if API key is available."""
    api_key = os.getenv("TIINGO_API_KEY")
    if api_key:
        try:
            config = {"session": True, "api_key": api_key}
            return TiingoClient(config)
        except Exception as e:
            logger.warning(f"Failed to initialize Tiingo client: {e}")
    return None


def get_llm():
    """Get LLM for sentiment analysis (optimized for speed)."""
    from maverick_mcp.providers.llm_factory import get_llm as get_llm_factory
    from maverick_mcp.providers.openrouter_provider import TaskType

    # Use sentiment analysis task type with fast preference
    return get_llm_factory(
        task_type=TaskType.SENTIMENT_ANALYSIS, prefer_fast=True, prefer_cheap=True
    )


async def get_news_sentiment_enhanced(
    ticker: str, timeframe: str = "7d", limit: int = 10
) -> dict[str, Any]:
    """
    Enhanced news sentiment analysis using Tiingo News API or LLM analysis.

    This tool provides reliable sentiment analysis by:
    1. First attempting to use Tiingo's news API (if available)
    2. Analyzing news sentiment using LLM if news is retrieved
    3. Falling back to research-based sentiment if Tiingo unavailable
    4. Providing guaranteed responses with appropriate fallbacks

    Args:
        ticker: Stock ticker symbol
        timeframe: Time frame for news (1d, 7d, 30d, etc.)
        limit: Maximum number of news articles to analyze

    Returns:
        Dictionary containing news sentiment analysis with confidence scores
    """
    tool_logger = get_tool_logger("data_get_news_sentiment_enhanced")
    request_id = str(uuid.uuid4())

    try:
        # Step 1: Try Tiingo News API
        tool_logger.step("tiingo_check", f"Checking Tiingo News API for {ticker}")

        # Calculate date range from timeframe (needed by both Tiingo and fallback paths)
        end_date = datetime.now()
        days = int(timeframe.rstrip("d")) if timeframe.endswith("d") else 7
        start_date = end_date - timedelta(days=days)

        tiingo_client = get_tiingo_client()
        if tiingo_client:
            try:
                tool_logger.step(
                    "tiingo_fetch", f"Fetching news from Tiingo for {ticker}"
                )

                # Fetch news using Tiingo's get_news method
                news_articles = await asyncio.wait_for(
                    asyncio.to_thread(
                        tiingo_client.get_news,
                        tickers=[ticker],
                        startDate=start_date.strftime("%Y-%m-%d"),
                        endDate=end_date.strftime("%Y-%m-%d"),
                        limit=limit,
                        sortBy="publishedDate",
                        onlyWithTickers=True,
                    ),
                    timeout=10.0,
                )

                if news_articles:
                    tool_logger.step(
                        "llm_analysis",
                        f"Analyzing {len(news_articles)} articles with LLM",
                    )

                    # Analyze sentiment using LLM
                    sentiment_result = await _analyze_news_sentiment_with_llm(
                        news_articles, ticker, tool_logger
                    )

                    tool_logger.complete(
                        f"Tiingo news sentiment analysis completed for {ticker}"
                    )

                    return {
                        "ticker": ticker,
                        "sentiment": sentiment_result["overall_sentiment"],
                        "confidence": sentiment_result["confidence"],
                        "source": "tiingo_news_with_llm_analysis",
                        "status": "success",
                        "analysis": {
                            "articles_analyzed": len(news_articles),
                            "sentiment_breakdown": sentiment_result["breakdown"],
                            "key_themes": sentiment_result["themes"],
                            "recent_headlines": sentiment_result["headlines"][:3],
                        },
                        "timeframe": timeframe,
                        "request_id": request_id,
                        "timestamp": datetime.now().isoformat(),
                    }

            except TimeoutError:
                tool_logger.step(
                    "tiingo_timeout", "Tiingo API timed out, using fallback"
                )
            except Exception as e:
                # Check if it's a permissions issue (free tier doesn't include news)
                if (
                    "403" in str(e)
                    or "permission" in str(e).lower()
                    or "unauthorized" in str(e).lower()
                ):
                    tool_logger.step(
                        "tiingo_no_permission",
                        "Tiingo news not available (requires paid plan)",
                    )
                else:
                    tool_logger.step("tiingo_error", f"Tiingo error: {str(e)}")

        # Step 2: Fallback to research-based sentiment
        tool_logger.step("research_fallback", "Using research-based sentiment analysis")

        from maverick_mcp.api.routers.research import analyze_market_sentiment

        # Use research tools to gather sentiment
        result = await asyncio.wait_for(
            analyze_market_sentiment(
                topic=f"{ticker} stock news sentiment recent {timeframe}",
                timeframe="1w" if days <= 7 else "1m",
                persona="moderate",
            ),
            timeout=15.0,
        )

        if result.get("success", False):
            sentiment_data = result.get("sentiment_analysis", {})
            return {
                "ticker": ticker,
                "sentiment": _extract_sentiment_from_research(sentiment_data),
                "confidence": sentiment_data.get("sentiment_confidence", 0.5),
                "source": "research_based_sentiment",
                "status": "fallback_success",
                "analysis": {
                    "overall_sentiment": sentiment_data.get("overall_sentiment", {}),
                    "key_themes": sentiment_data.get("sentiment_themes", [])[:3],
                    "market_insights": sentiment_data.get("market_insights", [])[:2],
                },
                "timeframe": timeframe,
                "request_id": request_id,
                "timestamp": datetime.now().isoformat(),
                "message": "Using research-based sentiment (Tiingo news unavailable on free tier)",
            }

        # Step 3: Basic fallback
        return _provide_basic_sentiment_fallback(ticker, request_id)

    except Exception as e:
        tool_logger.error("sentiment_error", e, f"Sentiment analysis failed: {str(e)}")
        return _provide_basic_sentiment_fallback(ticker, request_id, str(e))


async def _analyze_news_sentiment_with_llm(
    news_articles: list, ticker: str, tool_logger
) -> dict[str, Any]:
    """Analyze news articles sentiment using LLM."""

    llm = get_llm()
    if not llm:
        # No LLM available, do basic analysis
        return _basic_news_analysis(news_articles)

    try:
        # Prepare news summary for LLM
        news_summary = []
        for article in news_articles[:10]:  # Limit to 10 most recent
            news_summary.append(
                {
                    "title": article.get("title", ""),
                    "description": article.get("description", "")[:200]
                    if article.get("description")
                    else "",
                    "publishedDate": article.get("publishedDate", ""),
                    "source": article.get("source", ""),
                }
            )

        # Create sentiment analysis prompt
        prompt = f"""Analyze the sentiment of these recent news articles about {ticker} stock.

News Articles:
{chr(10).join([f"- {a['title']} ({a['source']}, {a['publishedDate'][:10] if a['publishedDate'] else 'Unknown date'})" for a in news_summary[:5]])}

Provide a JSON response with:
1. overall_sentiment: "bullish", "bearish", or "neutral"
2. confidence: 0.0 to 1.0
3. breakdown: dict with counts of positive, negative, neutral articles
4. themes: list of 3 key themes from the news
5. headlines: list of 3 most important headlines

Response format:
{{"overall_sentiment": "...", "confidence": 0.X, "breakdown": {{"positive": X, "negative": Y, "neutral": Z}}, "themes": ["...", "...", "..."], "headlines": ["...", "...", "..."]}}"""

        # Get LLM analysis
        response = await asyncio.to_thread(lambda: llm.invoke(prompt).content)

        # Parse JSON response
        import json

        try:
            # Extract JSON from response (handle markdown code blocks)
            if "```json" in response:
                json_str = response.split("```json")[1].split("```")[0].strip()
            elif "```" in response:
                json_str = response.split("```")[1].split("```")[0].strip()
            elif "{" in response:
                # Find JSON object in response
                start = response.index("{")
                end = response.rindex("}") + 1
                json_str = response[start:end]
            else:
                json_str = response

            result = json.loads(json_str)

            # Ensure all required fields
            return {
                "overall_sentiment": result.get("overall_sentiment", "neutral"),
                "confidence": float(result.get("confidence", 0.5)),
                "breakdown": result.get(
                    "breakdown",
                    {"positive": 0, "negative": 0, "neutral": len(news_articles)},
                ),
                "themes": result.get(
                    "themes",
                    ["Market movement", "Company performance", "Industry trends"],
                ),
                "headlines": [a.get("title", "") for a in news_summary[:3]],
            }

        except (json.JSONDecodeError, ValueError) as e:
            tool_logger.step("llm_parse_error", f"Failed to parse LLM response: {e}")
            return _basic_news_analysis(news_articles)

    except Exception as e:
        tool_logger.step("llm_error", f"LLM analysis failed: {e}")
        return _basic_news_analysis(news_articles)


def _basic_news_analysis(news_articles: list) -> dict[str, Any]:
    """Basic sentiment analysis without LLM."""

    # Simple keyword-based sentiment
    positive_keywords = [
        "gain",
        "rise",
        "up",
        "beat",
        "exceed",
        "strong",
        "bull",
        "buy",
        "upgrade",
        "positive",
    ]
    negative_keywords = [
        "loss",
        "fall",
        "down",
        "miss",
        "below",
        "weak",
        "bear",
        "sell",
        "downgrade",
        "negative",
    ]

    positive_count = 0
    negative_count = 0
    neutral_count = 0

    for article in news_articles:
        title = (
            article.get("title", "") + " " + article.get("description", "")
        ).lower()

        pos_score = sum(1 for keyword in positive_keywords if keyword in title)
        neg_score = sum(1 for keyword in negative_keywords if keyword in title)

        if pos_score > neg_score:
            positive_count += 1
        elif neg_score > pos_score:
            negative_count += 1
        else:
            neutral_count += 1

    total = len(news_articles)
    if total == 0:
        return {
            "overall_sentiment": "neutral",
            "confidence": 0.0,
            "breakdown": {"positive": 0, "negative": 0, "neutral": 0},
            "themes": [],
            "headlines": [],
        }

    # Determine overall sentiment
    if positive_count > negative_count * 1.5:
        overall = "bullish"
    elif negative_count > positive_count * 1.5:
        overall = "bearish"
    else:
        overall = "neutral"

    # Calculate confidence based on consensus
    max_count = max(positive_count, negative_count, neutral_count)
    confidence = max_count / total if total > 0 else 0.0

    return {
        "overall_sentiment": overall,
        "confidence": confidence,
        "breakdown": {
            "positive": positive_count,
            "negative": negative_count,
            "neutral": neutral_count,
        },
        "themes": ["Recent news", "Market activity", "Company updates"],
        "headlines": [a.get("title", "") for a in news_articles[:3]],
    }


def _extract_sentiment_from_research(sentiment_data: dict) -> str:
    """Extract simple sentiment direction from research data."""

    overall = sentiment_data.get("overall_sentiment", {})

    # Check for sentiment keywords
    if isinstance(overall, dict):
        sentiment_str = str(overall).lower()
    else:
        sentiment_str = str(overall).lower()

    if "bullish" in sentiment_str or "positive" in sentiment_str:
        return "bullish"
    elif "bearish" in sentiment_str or "negative" in sentiment_str:
        return "bearish"

    # Check confidence for direction
    confidence = sentiment_data.get("sentiment_confidence", 0.5)
    if confidence > 0.6:
        return "bullish"
    elif confidence < 0.4:
        return "bearish"

    return "neutral"


def _provide_basic_sentiment_fallback(
    ticker: str, request_id: str, error_detail: str = None
) -> dict[str, Any]:
    """Provide basic fallback when all methods fail."""

    response = {
        "ticker": ticker,
        "sentiment": "neutral",
        "confidence": 0.0,
        "source": "fallback",
        "status": "all_methods_failed",
        "message": "Unable to fetch news sentiment - returning neutral baseline",
        "analysis": {
            "note": "Consider using a paid Tiingo plan for news access or check API keys"
        },
        "request_id": request_id,
        "timestamp": datetime.now().isoformat(),
    }

    if error_detail:
        response["error_detail"] = error_detail[:200]  # Limit error message length

    return response
