"""
News aggregation search strategy for LDR.
Uses optimized prompts and search patterns for news aggregation.
"""

from typing import List, Dict, Any, Optional
from datetime import datetime, UTC
from loguru import logger

from .base_strategy import BaseSearchStrategy
from ..questions.news_question import NewsQuestionGenerator
from ...database.thread_local_session import thread_cleanup
from ...utilities.json_utils import extract_json


class NewsAggregationStrategy(BaseSearchStrategy):
    """
    Specialized search strategy for news aggregation.
    Uses single iteration with multiple parallel searches for broad coverage.
    """

    def __init__(self, model, search, all_links_of_system=None, **kwargs):
        super().__init__(all_links_of_system=all_links_of_system)
        self.model = model
        self.search = search
        self.strategy_name = "news_aggregation"
        self.max_iterations = 1  # News needs broad coverage, not deep iteration
        self.questions_per_iteration = 8  # More parallel searches for news
        self.question_generator = NewsQuestionGenerator(self.model)

    def generate_questions(self, query: str, context: str) -> List[str]:
        """Generate news-specific search queries using the NewsQuestionGenerator"""
        return self.question_generator.generate_questions(
            current_knowledge=context,
            query=query,
            questions_per_iteration=self.questions_per_iteration,
            questions_by_iteration=self.questions_by_iteration,
        )

    async def analyze_findings(
        self, all_findings: List[Dict]
    ) -> Dict[str, Any]:
        """Analyze search results to extract and structure news items"""

        if not all_findings:
            return {
                "status": "No news found",
                "news_items": [],
                "answer": "No significant news stories found for the specified criteria.",
            }

        # Format findings for LLM analysis
        snippets = []
        for i, finding in enumerate(
            all_findings[:50]
        ):  # Limit to 50 for token efficiency
            snippet = {
                "id": i + 1,
                "url": finding.get("url", ""),
                "title": finding.get("title", ""),
                "snippet": finding.get("snippet", "")[:300]
                if finding.get("snippet")
                else "",
                "content": finding.get("content", "")[:500]
                if finding.get("content")
                else "",
            }
            snippets.append(snippet)

        # Create structured prompt for news extraction
        prompt = self._create_news_analysis_prompt(snippets)

        try:
            response = self.model.invoke(prompt)
            content = (
                response.content
                if hasattr(response, "content")
                else str(response)
            )

            # Extract JSON from response
            news_data = self._extract_json_from_response(content)

            if news_data and "news_items" in news_data:
                return {
                    "status": "Success",
                    "news_items": news_data["news_items"],
                    "answer": self._format_news_summary(
                        news_data["news_items"]
                    ),
                }
            # Fallback to simple extraction
            return self._fallback_news_extraction(snippets)

        except Exception:
            logger.exception("Error analyzing news findings")
            return self._fallback_news_extraction(snippets)

    def _create_news_analysis_prompt(self, snippets: List[Dict]) -> str:
        """Create the analysis prompt for news extraction"""

        snippet_text = "\n\n".join(
            [
                f"[{s['id']}] Source: {s['url']}\n"
                f"Title: {s['title']}\n"
                f"Content: {s['snippet'] or s['content']}"
                for s in snippets
            ]
        )

        return f"""
Analyze these news snippets from search results and create a structured news report.
Today's date: {datetime.now(UTC).strftime("%B %d, %Y")}

{snippet_text}

Create a structured JSON response with the 10 most important news stories:
{{
    "news_items": [
        {{
            "headline": "8 words max describing the story",
            "category": "War/Security/Economy/Tech/Politics/Health/Environment/Other",
            "source_url": "url from snippets",
            "source_id": "[number] from above",
            "summary": "3 clear sentences about what happened",
            "analysis": "Why this matters and what happens next (2 sentences)",
            "impact_score": 1-10,
            "entities": {{"people": ["names"], "places": ["locations"], "orgs": ["organizations"]}},
            "topics": ["topic1", "topic2"],
            "time_ago": "estimated time (2 hours ago, yesterday, etc)",
            "is_developing": true/false,
            "surprising_element": "what makes this unexpected or notable (if any)"
        }}
    ]
}}

PRIORITIZE:
1. Stories with casualties or significant human impact
2. Economic impacts over $1 billion
3. Major political or diplomatic developments
4. Unexpected or surprising events
5. Breaking developments from the last 24 hours

Only include stories that are truly newsworthy and significant.
Ensure variety across different categories when possible.
"""

    def _extract_json_from_response(self, content: str) -> Optional[Dict]:
        """Extract JSON from LLM response"""
        result = extract_json(content, expected_type=dict)
        if isinstance(result, dict):
            return result
        return None

    def _format_news_summary(self, news_items: List[Dict]) -> str:
        """Format news items into a readable summary"""

        if not news_items:
            return "No significant news stories found."

        # Group by category
        by_category: Dict[str, List[Dict]] = {}
        for item in news_items:
            cat = item.get("category", "Other")
            if cat not in by_category:
                by_category[cat] = []
            by_category[cat].append(item)

        # Build summary
        parts = [f"Found {len(news_items)} significant news stories:\n"]

        for category, items in by_category.items():
            parts.append(f"\n**{category}** ({len(items)} stories):")
            for item in items[:3]:  # Top 3 per category
                parts.append(
                    f"- {item['headline']} "
                    f"(Impact: {item.get('impact_score', 'N/A')}/10)"
                )

        # Add top story details
        if news_items:
            top_story = max(news_items, key=lambda x: x.get("impact_score", 0))
            parts.append(f"\n**Top Story**: {top_story['headline']}")
            parts.append(f"{top_story.get('summary', 'No summary available')}")

        return "\n".join(parts)

    def _fallback_news_extraction(self, snippets: List[Dict]) -> Dict[str, Any]:
        """Simple fallback extraction when JSON parsing fails"""

        news_items = []
        for s in snippets[:10]:
            if s["title"] and len(s["title"]) > 10:
                news_items.append(
                    {
                        "headline": s["title"][:60],
                        "category": "Other",
                        "source_url": s["url"],
                        "summary": s["snippet"] or "No summary available",
                        "impact_score": 5,
                    }
                )

        return {
            "status": "Fallback extraction",
            "news_items": news_items,
            "answer": f"Found {len(news_items)} news stories (simplified extraction)",
        }

    def analyze_topic(self, query: str) -> Dict:
        """
        Analyze a topic for news aggregation.

        Args:
            query: The news query or focus area

        Returns:
            Dict containing news findings and formatted output
        """
        import asyncio

        # Generate news-specific search queries
        questions = self.generate_questions(query, "")
        self.questions_by_iteration[0] = questions

        all_findings = []

        # Search for each question
        for i, question in enumerate(questions):
            self._update_progress(
                f"Searching for: {question}",
                int((i / len(questions)) * 50),
                {"phase": "search", "question": question},
            )

            try:
                if self.search:
                    results = self.search.run(question)
                    if results:
                        all_findings.extend(results)
            except Exception:
                logger.exception("Search error")
                continue

        # Analyze findings - handle both sync and async contexts
        try:
            # Check if we're already in an async context
            asyncio.get_running_loop()
            # We're in an async context — run in a thread to avoid nesting
            import concurrent.futures

            # threading.local is NOT inherited by the pool worker, so the
            # PEP-578 audit-hook backstop armed on this (research) thread would
            # be inactive for the LLM analysis below. Capture and re-arm it in
            # the worker so the secondary egress net keeps parity under
            # PRIVATE_ONLY/STRICT (the primary LLM PEP still gates regardless).
            from ...security.egress.audit_hook import (
                active_egress_context,
                get_active_context,
            )

            _egress_ctx = get_active_context()

            def _analyze_with_egress():
                with active_egress_context(_egress_ctx):
                    return asyncio.run(self.analyze_findings(all_findings))

            with concurrent.futures.ThreadPoolExecutor() as executor:
                future = executor.submit(thread_cleanup(_analyze_with_egress))
                analysis = future.result()
        except RuntimeError:
            # No running event loop — asyncio.run() creates one, runs the
            # coroutine, drains pending tasks, and closes the loop automatically.
            analysis = asyncio.run(self.analyze_findings(all_findings))

        return {
            "findings": all_findings,
            "iterations": 1,
            "questions": self.questions_by_iteration,
            "formatted_findings": analysis.get("answer", "No news found"),
            "current_knowledge": analysis.get("answer", ""),
            "news_items": analysis.get("news_items", []),
            "status": analysis.get("status", "Unknown"),
        }
