import json

from typing import Any, Dict, List

import numpy as np
import pandas as pd
from langchain_core.messages import HumanMessage

from plutex.graph.state import AgentState, show_agent_reasoning
from plutex.tools.api import get_company_news, get_insider_trades
from plutex.utils.progress import progress


##### Sentiment Agent #####
def sentiment_agent(state: AgentState):
    """Analyzes market sentiment and generates trading signals for multiple tickers."""
    data: Dict[str, Any] = state.get("data", {})
    end_date: str | None = data.get("end_date")
    tickers: List[str] | None = data.get("tickers")

    # Initialize sentiment analysis for each ticker
    sentiment_analysis = {}

    # --- Input Validation ---
    if not tickers or not isinstance(tickers, list):
        progress.update_status(
            "sentiment_agent", None, "Failed: Invalid or missing tickers list"
        )
        # Return current state if inputs are invalid
        return {"messages": state["messages"], "data": data}

    if not end_date or not isinstance(end_date, str):
        progress.update_status(
            "sentiment_agent", None, "Failed: Invalid or missing end_date"
        )
        # Return current state if inputs are invalid
        return {"messages": state["messages"], "data": data}
    # --- End Input Validation ---

    for ticker in tickers:
        progress.update_status("sentiment_agent", ticker, "Fetching insider trades")

        # Get the insider trades
        insider_trades = get_insider_trades(
            ticker=ticker,
            end_date=end_date,
            limit=1000,
        )

        progress.update_status("sentiment_agent", ticker, "Analyzing trading patterns")

        # Get the signals from the insider trades
        transaction_shares = pd.Series(
            [t.transaction_shares for t in insider_trades]
        ).dropna()
        insider_signals = np.where(
            transaction_shares < 0, "bearish", "bullish"
        ).tolist()

        progress.update_status("sentiment_agent", ticker, "Fetching company news")

        # Get the company news
        company_news = get_company_news(ticker, end_date, limit=100)

        # Get the sentiment from the company news
        sentiment = pd.Series([n.sentiment for n in company_news]).dropna()
        news_signals = np.where(
            sentiment == "negative",
            "bearish",
            np.where(sentiment == "positive", "bullish", "neutral"),
        ).tolist()

        progress.update_status("sentiment_agent", ticker, "Combining signals")
        # Combine signals from both sources with weights
        insider_weight = 0.3
        news_weight = 0.7

        # Calculate weighted signal counts
        bullish_signals = (
            insider_signals.count("bullish") * insider_weight
            + news_signals.count("bullish") * news_weight
        )
        bearish_signals = (
            insider_signals.count("bearish") * insider_weight
            + news_signals.count("bearish") * news_weight
        )

        if bullish_signals > bearish_signals:
            overall_signal = "bullish"
        elif bearish_signals > bullish_signals:
            overall_signal = "bearish"
        else:
            overall_signal = "neutral"

        # Calculate confidence level based on the weighted proportion
        total_weighted_signals = (
            len(insider_signals) * insider_weight + len(news_signals) * news_weight
        )
        confidence = 0  # Default confidence when there are no signals
        if total_weighted_signals > 0:
            confidence = (
                round(max(bullish_signals, bearish_signals) / total_weighted_signals, 2)
                * 100
            )
        reasoning = f"Weighted Bullish signals: {bullish_signals:.1f}, Weighted Bearish signals: {bearish_signals:.1f}"

        sentiment_analysis[ticker] = {
            "signal": overall_signal,
            "confidence": confidence,
            "reasoning": reasoning,
        }

        progress.update_status("sentiment_agent", ticker, "Done")

    # Create the sentiment message
    message = HumanMessage(
        content=json.dumps(sentiment_analysis),
        name="sentiment_agent",
    )

    # Print the reasoning if the flag is set
    if state["metadata"]["show_reasoning"]:
        show_agent_reasoning(sentiment_analysis, "Sentiment Analysis Agent")

    # Add the signal to the analyst_signals list
    state["data"]["analyst_signals"]["sentiment_agent"] = sentiment_analysis

    return {
        "messages": [message],
        "data": data,
    }
