"""BullResearcher Agent implementation for OpenClaw trading system.

This module provides the BullResearcher class that generates bullish investment
theses by analyzing technical, sentiment, and fundamental reports. It extracts
positive factors, generates counter-arguments to bearish views, and produces
comprehensive bull case reports.
"""

from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional

from openclaw.agents.base import ActivityType, BaseAgent
from openclaw.core.economy import SurvivalStatus


@dataclass
class BullReport:
    """Bullish research report generated by BullResearcher.

    Contains the bull case analysis including positive factors,
    counter-arguments to bearish points, price targets, and conviction.

    Attributes:
        symbol: The trading symbol analyzed (e.g., "AAPL")
        bullish_factors: List of positive factors supporting the bull case
        counter_arguments: Mapping of bearish points to their counter-arguments
        price_target: Target price based on bullish analysis
        conviction_level: Confidence level in the bull case (0.0 to 1.0)
        summary: Executive summary of the bull case
        risk_factors: List of risks that could invalidate the bull case
        catalysts: List of potential catalysts that could drive price higher
    """

    symbol: str
    bullish_factors: List[str] = field(default_factory=list)
    counter_arguments: Dict[str, str] = field(default_factory=dict)
    price_target: float = 0.0
    conviction_level: float = 0.5
    summary: str = ""
    risk_factors: List[str] = field(default_factory=list)
    catalysts: List[str] = field(default_factory=list)

    def __post_init__(self):
        """Validate conviction level is within bounds."""
        self.conviction_level = max(0.0, min(1.0, self.conviction_level))


class BullResearcher(BaseAgent):
    """Research agent that generates bullish investment theses.

    The BullResearcher analyzes technical, sentiment, and fundamental reports
to construct a comprehensive bull case. It extracts positive signals,
generates counter-arguments to bearish concerns, and produces actionable
bullish research reports.

    Decision cost: $0.15 per analysis

    Args:
        agent_id: Unique identifier for this agent
        initial_capital: Starting balance for the agent
        skill_level: Initial skill level (0.0 to 1.0)
    """

    decision_cost: float = 0.15

    def __init__(
        self,
        agent_id: str,
        initial_capital: float,
        skill_level: float = 0.5,
    ):
        super().__init__(agent_id, initial_capital, skill_level)
        self._last_report: Optional[BullReport] = None
        self._report_history: List[BullReport] = []

    async def decide_activity(self) -> ActivityType:
        """Decide what activity to perform based on economic status.

        The BullResearcher primarily performs analysis but will rest or
        paper trade when in critical financial condition.

        Returns:
            The activity type to perform
        """
        status = self.survival_status

        # Bankrupt agents can only rest
        if status == SurvivalStatus.BANKRUPT:
            self.logger.warning("Agent is bankrupt, resting...")
            return ActivityType.REST

        # Critical status - focus on learning/paper trading
        if status == SurvivalStatus.CRITICAL:
            if self.skill_level < 0.7:
                return ActivityType.LEARN
            return ActivityType.PAPER_TRADE

        # Struggling - more paper analysis
        if status == SurvivalStatus.STRUGGLING:
            # Simulate paper analysis as paper trade
            return ActivityType.PAPER_TRADE

        # Stable and thriving - perform real analysis
        return ActivityType.ANALYZE

    async def analyze(self, symbol: str) -> Dict[str, Any]:
        """Analyze a trading symbol and generate bullish perspective.

        This method deducts the decision cost and generates a bull case
        analysis. Note: This is a simplified version that generates
        generic bullish factors. In production, this would integrate
        with TechnicalAnalyst, SentimentAnalyst, and FundamentalAnalyst.

        Args:
            symbol: The symbol to analyze (e.g., "AAPL")

        Returns:
            Dictionary containing bull case analysis
        """
        # Deduct decision cost
        cost = self._deduct_decision_cost()
        self.logger.info(f"Bull analysis cost for {symbol}: ${cost:.2f}")

        # Generate bull case (placeholder implementation)
        # In production, this would call generate_bull_case with actual reports
        bull_report = self._generate_generic_bull_case(symbol)
        self._last_report = bull_report
        self._report_history.append(bull_report)

        return {
            "symbol": symbol,
            "bull_report": {
                "bullish_factors": bull_report.bullish_factors,
                "counter_arguments": bull_report.counter_arguments,
                "price_target": bull_report.price_target,
                "conviction_level": bull_report.conviction_level,
                "summary": bull_report.summary,
                "risk_factors": bull_report.risk_factors,
                "catalysts": bull_report.catalysts,
            },
            "cost": cost,
        }

    async def generate_bull_case(
        self,
        symbol: str,
        technical_report: Optional[Any] = None,
        sentiment_report: Optional[Any] = None,
        fundamental_report: Optional[Any] = None,
    ) -> BullReport:
        """Generate a comprehensive bull case from analyst reports.

        This is the main method for generating bullish research. It analyzes
        inputs from technical, sentiment, and fundamental analysts to
        construct a comprehensive bull case with counter-arguments.

        Args:
            symbol: The trading symbol to analyze
            technical_report: Technical analysis report (optional)
            sentiment_report: Sentiment analysis report (optional)
            fundamental_report: Fundamental analysis report (optional)

        Returns:
            BullReport containing the bull case analysis
        """
        # Deduct decision cost
        cost = self._deduct_decision_cost()
        self.logger.info(
            f"Generating bull case for {symbol} (cost: ${cost:.2f})"
        )

        # Extract bullish factors from reports
        bullish_factors = self._extract_bullish_factors(
            technical_report, sentiment_report, fundamental_report
        )

        # Generate counter-arguments to potential bearish points
        counter_arguments = self._generate_counter_arguments(
            technical_report, sentiment_report, fundamental_report
        )

        # Calculate conviction level based on factor strength
        conviction = self._calculate_conviction(
            bullish_factors, technical_report, sentiment_report, fundamental_report
        )

        # Generate price target
        price_target = self._generate_price_target(
            symbol, fundamental_report, technical_report
        )

        # Identify catalysts
        catalysts = self._identify_catalysts(
            technical_report, sentiment_report, fundamental_report
        )

        # Identify risks
        risk_factors = self._identify_risks(
            technical_report, sentiment_report, fundamental_report
        )

        # Generate summary
        summary = self._generate_summary(symbol, bullish_factors, conviction)

        bull_report = BullReport(
            symbol=symbol,
            bullish_factors=bullish_factors,
            counter_arguments=counter_arguments,
            price_target=price_target,
            conviction_level=conviction,
            summary=summary,
            risk_factors=risk_factors,
            catalysts=catalysts,
        )

        self._last_report = bull_report
        self._report_history.append(bull_report)

        self.logger.info(
            f"Bull case generated for {symbol}: "
            f"conviction={conviction:.1%}, target=${price_target:.2f}"
        )

        return bull_report

    def _deduct_decision_cost(self) -> float:
        """Deduct the fixed decision cost from agent balance.

        Returns:
            The cost amount deducted
        """
        self.economic_tracker._update_balance(
            -self.decision_cost,
            f"BullResearcher decision cost"
        )
        return self.decision_cost

    def _extract_bullish_factors(
        self,
        technical_report: Optional[Any],
        sentiment_report: Optional[Any],
        fundamental_report: Optional[Any],
    ) -> List[str]:
        """Extract bullish factors from analyst reports.

        Args:
            technical_report: Technical analysis report
            sentiment_report: Sentiment analysis report
            fundamental_report: Fundamental analysis report

        Returns:
            List of bullish factor descriptions
        """
        factors = []

        # Extract from technical report
        if technical_report is not None:
            factors.extend(self._extract_technical_bullish_factors(technical_report))

        # Extract from sentiment report
        if sentiment_report is not None:
            factors.extend(self._extract_sentiment_bullish_factors(sentiment_report))

        # Extract from fundamental report
        if fundamental_report is not None:
            factors.extend(self._extract_fundamental_bullish_factors(fundamental_report))

        # If no reports provided, return placeholder factors
        if not factors:
            factors = ["Analysis pending - awaiting analyst reports"]

        return factors

    def _extract_technical_bullish_factors(self, report: Any) -> List[str]:
        """Extract bullish factors from technical report.

        Args:
            report: Technical analysis report

        Returns:
            List of technical bullish factors
        """
        factors = []

        # Handle dictionary format
        if isinstance(report, dict):
            trend = report.get("trend", "").lower()
            indicators = report.get("indicators", {})

            if trend == "uptrend":
                factors.append("Technical: Price in established uptrend")

            rsi = indicators.get("rsi")
            if rsi is not None and rsi < 40:
                factors.append(f"Technical: RSI at {rsi:.1f} suggests oversold conditions")

            macd = indicators.get("macd")
            if macd is not None and macd > 0:
                factors.append("Technical: Positive MACD momentum")

        # Handle object format (e.g., MarketAnalysis)
        elif hasattr(report, "trend"):
            if report.trend == "uptrend":
                factors.append("Technical: Price in established uptrend")

            if hasattr(report, "indicators"):
                indicators = report.indicators
                if isinstance(indicators, dict):
                    rsi = indicators.get("rsi")
                    if rsi is not None and rsi < 40:
                        factors.append(f"Technical: RSI at {rsi:.1f} suggests oversold conditions")

                    macd = indicators.get("macd")
                    if macd is not None and macd > 0:
                        factors.append("Technical: Positive MACD momentum")

        return factors

    def _extract_sentiment_bullish_factors(self, report: Any) -> List[str]:
        """Extract bullish factors from sentiment report.

        Args:
            report: Sentiment analysis report

        Returns:
            List of sentiment bullish factors
        """
        factors = []

        if isinstance(report, dict):
            sentiment = report.get("sentiment", "").lower()
            score = report.get("score")

            if sentiment in ["bullish", "positive"]:
                factors.append("Sentiment: Overall market sentiment is bullish")

            if score is not None and score > 0.6:
                factors.append(f"Sentiment: High bullish sentiment score ({score:.2f})")

        elif hasattr(report, "sentiment"):
            if report.sentiment in ["bullish", "positive"]:
                factors.append("Sentiment: Overall market sentiment is bullish")

        return factors

    def _extract_fundamental_bullish_factors(self, report: Any) -> List[str]:
        """Extract bullish factors from fundamental report.

        Args:
            report: Fundamental analysis report

        Returns:
            List of fundamental bullish factors
        """
        factors = []

        if isinstance(report, dict):
            valuation = report.get("valuation", "").lower()

            if valuation in ["undervalued", "cheap"]:
                factors.append("Fundamental: Stock appears undervalued")

            growth = report.get("growth_rate")
            if growth is not None and growth > 0.15:
                factors.append(f"Fundamental: Strong growth rate ({growth:.1%})")

            pe_ratio = report.get("pe_ratio")
            if pe_ratio is not None and pe_ratio < 20:
                factors.append(f"Fundamental: Attractive P/E ratio ({pe_ratio:.1f})")

        elif hasattr(report, "valuation"):
            if report.valuation in ["undervalued", "cheap"]:
                factors.append("Fundamental: Stock appears undervalued")

        return factors

    def _generate_counter_arguments(
        self,
        technical_report: Optional[Any],
        sentiment_report: Optional[Any],
        fundamental_report: Optional[Any],
    ) -> Dict[str, str]:
        """Generate counter-arguments to bearish points.

        Args:
            technical_report: Technical analysis report
            sentiment_report: Sentiment analysis report
            fundamental_report: Fundamental analysis report

        Returns:
            Dictionary mapping bearish points to counter-arguments
        """
        counter_args = {}

        # Common bearish arguments and their counters
        counter_args[
            "Stock is overbought"
        ] = "Overbought conditions can persist in strong uptrends; momentum often precedes fundamentals"

        counter_args[
            "Valuation is stretched"
        ] = "Premium valuation justified by superior growth trajectory and market leadership"

        counter_args[
            "Recent rally is unsustainable"
        ] = "Price action reflects improving fundamentals and institutional accumulation"

        counter_args[
            "Market sentiment is too optimistic"
        ] = "Bullish sentiment aligns with earnings acceleration and positive guidance"

        # Add report-specific counters
        if technical_report is not None:
            counter_args.update(self._generate_technical_counters(technical_report))

        if fundamental_report is not None:
            counter_args.update(self._generate_fundamental_counters(fundamental_report))

        return counter_args

    def _generate_technical_counters(self, report: Any) -> Dict[str, str]:
        """Generate counter-arguments based on technical analysis.

        Args:
            report: Technical analysis report

        Returns:
            Dictionary of technical bearish points and counters
        """
        return {
            "Technical indicators show exhaustion": "Indicators resetting for next leg higher; volume profile supports continuation"
        }

    def _generate_fundamental_counters(self, report: Any) -> Dict[str, str]:
        """Generate counter-arguments based on fundamental analysis.

        Args:
            report: Fundamental analysis report

        Returns:
            Dictionary of fundamental bearish points and counters
        """
        return {
            "Earnings growth is slowing": "Growth deceleration is temporary; new products/services will reaccelerate growth"
        }

    def _calculate_conviction(
        self,
        bullish_factors: List[str],
        technical_report: Optional[Any],
        sentiment_report: Optional[Any],
        fundamental_report: Optional[Any],
    ) -> float:
        """Calculate conviction level based on factor strength.

        Args:
            bullish_factors: List of identified bullish factors
            technical_report: Technical analysis report
            sentiment_report: Sentiment analysis report
            fundamental_report: Fundamental analysis report

        Returns:
            Conviction level between 0.0 and 1.0
        """
        base_conviction = 0.5

        # More factors = higher conviction (up to a point)
        factor_boost = min(len(bullish_factors) * 0.05, 0.2)

        # Skill level affects conviction accuracy
        skill_boost = self.skill_level * 0.2

        conviction = base_conviction + factor_boost + skill_boost

        # Cap at 0.9 unless we have all three report types
        max_conviction = 0.75
        if all(r is not None for r in [technical_report, sentiment_report, fundamental_report]):
            max_conviction = 0.9

        return min(conviction, max_conviction)

    def _generate_price_target(
        self,
        symbol: str,
        fundamental_report: Optional[Any],
        technical_report: Optional[Any],
    ) -> float:
        """Generate price target based on analysis.

        Args:
            symbol: Trading symbol
            fundamental_report: Fundamental analysis report
            technical_report: Technical analysis report

        Returns:
            Target price
        """
        # Try to extract current price from reports
        current_price = 100.0  # Default

        if technical_report is not None:
            if isinstance(technical_report, dict):
                indicators = technical_report.get("indicators", {})
                current_price = indicators.get("current_price", current_price)
            elif hasattr(technical_report, "indicators"):
                if isinstance(technical_report.indicators, dict):
                    current_price = technical_report.indicators.get("current_price", current_price)

        # Apply upside based on conviction and reports available
        upside = 0.10 + (self.skill_level * 0.10)  # 10-20% base upside

        if fundamental_report is not None:
            upside += 0.05  # Additional 5% for fundamental backing

        return round(current_price * (1 + upside), 2)

    def _identify_catalysts(
        self,
        technical_report: Optional[Any],
        sentiment_report: Optional[Any],
        fundamental_report: Optional[Any],
    ) -> List[str]:
        """Identify potential catalysts that could drive price higher.

        Args:
            technical_report: Technical analysis report
            sentiment_report: Sentiment analysis report
            fundamental_report: Fundamental analysis report

        Returns:
            List of potential catalysts
        """
        catalysts = [
            "Earnings report beat expectations",
            "Positive guidance raise",
            "Institutional accumulation",
            "Technical breakout above resistance",
        ]

        # Add skill-based catalyst identification
        if self.skill_level > 0.7:
            catalysts.append("Sector rotation favoring this industry")

        return catalysts

    def _identify_risks(
        self,
        technical_report: Optional[Any],
        sentiment_report: Optional[Any],
        fundamental_report: Optional[Any],
    ) -> List[str]:
        """Identify risks that could invalidate the bull case.

        Args:
            technical_report: Technical analysis report
            sentiment_report: Sentiment analysis report
            fundamental_report: Fundamental analysis report

        Returns:
            List of risk factors
        """
        risks = [
            "Broader market correction",
            "Earnings miss or negative guidance",
            "Sector-specific headwinds",
        ]

        if self.skill_level > 0.6:
            risks.append("Regulatory changes affecting the industry")

        return risks

    def _generate_summary(
        self, symbol: str, bullish_factors: List[str], conviction: float
    ) -> str:
        """Generate executive summary of the bull case.

        Args:
            symbol: Trading symbol
            bullish_factors: List of bullish factors
            conviction: Conviction level

        Returns:
            Summary string
        """
        factor_count = len(bullish_factors)

        if conviction > 0.75:
            conviction_desc = "high"
        elif conviction > 0.6:
            conviction_desc = "moderate"
        else:
            conviction_desc = "cautious"

        return (
            f"Bull case for {symbol} with {conviction_desc} conviction ({conviction:.0%}). "
            f"Identified {factor_count} supporting factors. "
            f"Recommendation: Accumulate on weakness with stop-loss discipline."
        )

    def _generate_generic_bull_case(self, symbol: str) -> BullReport:
        """Generate a generic bull case when no reports are available.

        Args:
            symbol: Trading symbol

        Returns:
            BullReport with generic analysis
        """
        return BullReport(
            symbol=symbol,
            bullish_factors=["Analysis pending - awaiting detailed reports"],
            counter_arguments={
                "General market concerns": "Market conditions remain supportive for quality names"
            },
            price_target=0.0,
            conviction_level=0.5,
            summary=f"Awaiting comprehensive analysis for {symbol}",
            risk_factors=["Market volatility", "Economic uncertainty"],
            catalysts=["Earnings results", "Sector momentum"],
        )

    def get_last_report(self) -> Optional[BullReport]:
        """Get the most recent bull report.

        Returns:
            The last BullReport generated, or None if no reports exist
        """
        return self._last_report

    def get_report_history(self) -> List[BullReport]:
        """Get all generated bull reports.

        Returns:
            List of all BullReports generated by this agent
        """
        return self._report_history.copy()

    def get_bullish_recommendation(self, symbol: str) -> Dict[str, Any]:
        """Get a bullish recommendation with actionable insights.

        Args:
            symbol: Trading symbol

        Returns:
            Dictionary with recommendation details
        """
        if self._last_report is None or self._last_report.symbol != symbol:
            return {
                "symbol": symbol,
                "recommendation": "HOLD",
                "reason": "No recent analysis available",
                "conviction": 0.0,
            }

        report = self._last_report

        # Generate recommendation based on conviction
        if report.conviction_level > 0.75:
            rec = "STRONG_BUY"
        elif report.conviction_level > 0.6:
            rec = "BUY"
        elif report.conviction_level > 0.45:
            rec = "ACCUMULATE"
        else:
            rec = "HOLD"

        return {
            "symbol": symbol,
            "recommendation": rec,
            "price_target": report.price_target,
            "conviction": report.conviction_level,
            "key_factors": report.bullish_factors[:3],
            "risks": report.risk_factors[:2],
        }
