"""
OSINT Agent - Dark web reconnaissance and threat intelligence gathering

Provides autonomous OSINT investigation capabilities including:
- Dark web search across multiple engines
- Content scraping via Tor proxy
- LLM-powered analysis and artifact extraction
- Intelligence report generation
"""

import time
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Optional

from groknroll.agents.base_agent import AgentCapability, AgentConfig, AgentResponse, BaseAgent
from groknroll.osint.artifact_extractor import Artifact, ArtifactExtractor
from groknroll.osint.content_scraper import ContentScraper, ScrapedContent
from groknroll.osint.dark_web_search import DarkWebSearch, SearchResult
from groknroll.osint.intelligence_analyzer import IntelligenceAnalyzer, IntelligenceReport
from groknroll.osint.report_generator import ReportGenerator
from groknroll.osint.tor_manager import TorManager


@dataclass
class InvestigationResult:
    """Result of an OSINT investigation."""

    query: str
    refined_query: str
    search_results: list[SearchResult]
    scraped_contents: list[ScrapedContent]
    artifacts: list[Artifact]
    report: Optional[IntelligenceReport]
    report_path: Optional[Path]
    duration: float
    status: str
    error: Optional[str] = None

    def __post_init__(self):
        """Calculate summary statistics."""
        self.results_count = len(self.search_results)
        self.scraped_count = len([c for c in self.scraped_contents if c.success])
        self.artifacts_count = len(self.artifacts)

    def to_dict(self) -> dict:
        """Convert to dictionary."""
        return {
            "query": self.query,
            "refined_query": self.refined_query,
            "results_count": self.results_count,
            "scraped_count": self.scraped_count,
            "artifacts_count": self.artifacts_count,
            "duration": self.duration,
            "status": self.status,
            "error": self.error,
            "report_path": str(self.report_path) if self.report_path else None,
        }


class OsintAgent(BaseAgent):
    """
    OSINT reconnaissance agent for dark web intelligence gathering.

    Capabilities:
    - Dark web search across 16 search engines via Tor
    - Content scraping with retry logic
    - LLM-powered query refinement and analysis
    - Artifact extraction (emails, crypto, domains, etc.)
    - Intelligence report generation

    Usage:
        osint = OsintAgent(project_path=Path("."))
        result = osint.investigate("ransomware group lockbit", deep=True)
        print(result.report)

    Requires Tor daemon running on localhost:9050.
    """

    def __init__(
        self,
        project_path: Path,
        backend: str = "anthropic",
        model: str = "claude-sonnet-4-20250514",
        max_cost: float = 10.0,
        timeout: int = 600,
        tor_host: str = "127.0.0.1",
        tor_port: int = 9050,
        max_workers: int = 5,
        output_dir: Optional[Path] = None,
    ):
        """
        Initialize OSINT Agent.

        Args:
            project_path: Project root path
            backend: LLM backend ('anthropic', 'openai', 'google')
            model: LLM model identifier
            max_cost: Maximum cost limit for LLM calls
            timeout: Investigation timeout in seconds
            tor_host: Tor SOCKS5 proxy host
            tor_port: Tor SOCKS5 proxy port
            max_workers: Maximum concurrent threads for search/scrape
            output_dir: Directory for saving reports
        """
        config = AgentConfig(
            name="osint",
            description="OSINT reconnaissance agent for dark web intelligence gathering",
            capabilities=[
                AgentCapability.ANALYZE_CODE,
                AgentCapability.SEARCH_CODE,
                AgentCapability.READ_FILES,
            ],
            model=model,
            max_cost=max_cost,
            timeout=timeout,
        )
        super().__init__(config, project_path)

        # Store configuration
        self.backend = backend
        self.tor_host = tor_host
        self.tor_port = tor_port
        self.max_workers = max_workers
        self.output_dir = output_dir or project_path / "osint_reports"

        # Initialize OSINT components (lazy loading)
        self._tor_manager: Optional[TorManager] = None
        self._search: Optional[DarkWebSearch] = None
        self._scraper: Optional[ContentScraper] = None
        self._analyzer: Optional[IntelligenceAnalyzer] = None
        self._extractor: Optional[ArtifactExtractor] = None
        self._reporter: Optional[ReportGenerator] = None

    @property
    def tor_manager(self) -> TorManager:
        """Get or create TorManager."""
        if self._tor_manager is None:
            self._tor_manager = TorManager(self.tor_host, self.tor_port)
        return self._tor_manager

    @property
    def search(self) -> DarkWebSearch:
        """Get or create DarkWebSearch."""
        if self._search is None:
            self._search = DarkWebSearch(self.tor_manager, self.max_workers)
        return self._search

    @property
    def scraper(self) -> ContentScraper:
        """Get or create ContentScraper."""
        if self._scraper is None:
            self._scraper = ContentScraper(self.tor_manager, self.max_workers)
        return self._scraper

    @property
    def analyzer(self) -> IntelligenceAnalyzer:
        """Get or create IntelligenceAnalyzer."""
        if self._analyzer is None:
            self._analyzer = IntelligenceAnalyzer(self.backend, self.config.model)
        return self._analyzer

    @property
    def extractor(self) -> ArtifactExtractor:
        """Get or create ArtifactExtractor."""
        if self._extractor is None:
            self._extractor = ArtifactExtractor()
        return self._extractor

    @property
    def reporter(self) -> ReportGenerator:
        """Get or create ReportGenerator."""
        if self._reporter is None:
            self._reporter = ReportGenerator(self.output_dir)
        return self._reporter

    def execute(self, task: str, context: Optional[dict[str, Any]] = None) -> AgentResponse:
        """
        Execute OSINT investigation task.

        Args:
            task: Investigation query or command
            context: Additional context (e.g., {'deep': True})

        Returns:
            AgentResponse with investigation results
        """
        start_time = time.time()

        try:
            # Parse context for options
            deep = context.get("deep", False) if context else False
            save_report = context.get("save_report", True) if context else True

            # Run investigation
            result = self.investigate(task, deep=deep, save_report=save_report)

            elapsed = time.time() - start_time

            response = AgentResponse(
                success=result.status == "complete",
                message=self._format_result_message(result),
                agent_name=self.config.name,
                task=task,
                cost=0.0,  # TODO: Track LLM costs
                time=elapsed,
                metadata=result.to_dict(),
            )

        except Exception as e:
            elapsed = time.time() - start_time
            response = AgentResponse(
                success=False,
                message=f"Investigation failed: {str(e)}",
                agent_name=self.config.name,
                task=task,
                cost=0.0,
                time=elapsed,
                metadata={"error": str(e)},
            )

        self._log_execution(response)
        return response

    def investigate(
        self,
        query: str,
        deep: bool = False,
        save_report: bool = True,
        engines: Optional[list[str]] = None,
    ) -> InvestigationResult:
        """
        Run full OSINT investigation pipeline.

        Pipeline:
        1. Verify Tor connectivity
        2. Refine query with LLM
        3. Search dark web engines
        4. Filter results with LLM (if deep)
        5. Scrape content from results
        6. Extract artifacts
        7. Analyze content with LLM (if deep)
        8. Generate report

        Args:
            query: Investigation query
            deep: If True, perform deeper analysis with more scraping and LLM analysis
            save_report: If True, save report to file
            engines: Optional list of specific engines to use

        Returns:
            InvestigationResult with all investigation data
        """
        start_time = time.time()

        # Step 1: Check Tor connectivity
        if not self.tor_manager.check_connection():
            return InvestigationResult(
                query=query,
                refined_query=query,
                search_results=[],
                scraped_contents=[],
                artifacts=[],
                report=None,
                report_path=None,
                duration=time.time() - start_time,
                status="failed",
                error="Tor proxy not available. Ensure Tor is running on {}:{}".format(
                    self.tor_host, self.tor_port
                ),
            )

        # Step 2: Refine query
        refined_query = self.analyzer.refine_query(query)

        # Step 3: Search dark web
        search_results = self.search.search(
            refined_query,
            engines=engines,
            limit=100 if deep else 50,
        )

        if not search_results:
            return InvestigationResult(
                query=query,
                refined_query=refined_query,
                search_results=[],
                scraped_contents=[],
                artifacts=[],
                report=None,
                report_path=None,
                duration=time.time() - start_time,
                status="complete",
                error="No search results found",
            )

        # Step 4: Filter results (deep mode only)
        if deep and len(search_results) > 20:
            search_results = self.analyzer.filter_results(
                search_results,
                query,
                top_k=30,
            )

        # Step 5: Scrape content
        urls_to_scrape = [r.url for r in search_results[: 30 if deep else 15]]
        scraped_contents = self.scraper.scrape_batch(urls_to_scrape)

        # Step 6: Extract artifacts
        all_artifacts: list[Artifact] = []
        for content in scraped_contents:
            if content.success and content.content:
                artifacts = self.extractor.extract_all(content.content, content.url)
                all_artifacts.extend(artifacts)

        # Step 7: Analyze content (deep mode only)
        intelligence_report: Optional[IntelligenceReport] = None
        if deep:
            artifact_dicts = [a.to_dict() for a in all_artifacts]
            intelligence_report = self.analyzer.analyze_content(
                scraped_contents,
                query,
                artifact_dicts,
            )
            intelligence_report.refined_query = refined_query

        # Step 8: Generate report
        report_path: Optional[Path] = None
        if save_report:
            markdown_report = self.reporter.generate(
                query=query,
                refined_query=refined_query,
                results=search_results,
                scraped_contents=scraped_contents,
                artifacts=all_artifacts,
                report=intelligence_report,
            )
            report_path = self.reporter.save(markdown_report)

        return InvestigationResult(
            query=query,
            refined_query=refined_query,
            search_results=search_results,
            scraped_contents=scraped_contents,
            artifacts=all_artifacts,
            report=intelligence_report,
            report_path=report_path,
            duration=time.time() - start_time,
            status="complete",
        )

    def quick_search(self, query: str, limit: int = 20) -> list[SearchResult]:
        """
        Quick search without scraping or analysis.

        Args:
            query: Search query
            limit: Maximum results

        Returns:
            List of SearchResult objects
        """
        if not self.tor_manager.check_connection():
            raise ConnectionError("Tor proxy not available")

        return self.search.search(query, limit=limit)

    def check_status(self) -> dict[str, Any]:
        """
        Check OSINT system status.

        Returns:
            Status dictionary with connectivity and configuration info
        """
        tor_connected = self.tor_manager.check_connection()
        tor_ip = self.tor_manager.get_tor_ip() if tor_connected else None

        return {
            "tor_connected": tor_connected,
            "tor_ip": tor_ip,
            "tor_proxy": f"{self.tor_host}:{self.tor_port}",
            "engines_available": len(self.search.engines),
            "engine_names": self.search.get_available_engines(),
            "backend": self.backend,
            "model": self.config.model,
            "max_workers": self.max_workers,
            "output_dir": str(self.output_dir),
        }

    def _format_result_message(self, result: InvestigationResult) -> str:
        """Format investigation result as message."""
        if result.error:
            return f"Investigation incomplete: {result.error}"

        lines = [
            "OSINT Investigation Complete",
            f"Query: {result.query}",
            f"Refined: {result.refined_query}",
            f"Results: {result.results_count} found, {result.scraped_count} scraped",
            f"Artifacts: {result.artifacts_count} extracted",
            f"Duration: {result.duration:.1f}s",
        ]

        if result.report_path:
            lines.append(f"Report: {result.report_path}")

        if result.report and result.report.key_findings:
            lines.append("\nKey Findings:")
            for finding in result.report.key_findings[:3]:
                lines.append(f"  - {finding}")

        return "\n".join(lines)
