"""Web scraping module for content research."""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Optional

from ..utils.logger import Logger

if TYPE_CHECKING:
    from .api_client import ApiClient


@dataclass
class ScrapeResult:
    """Result of web scraping."""

    success: bool
    markdown: str = ""
    html: str = ""
    links: list[str] = field(default_factory=list)
    metadata: dict = field(default_factory=dict)
    error: Optional[str] = None


@dataclass
class ExtractResult:
    """Result of structured data extraction."""

    success: bool
    data: dict = field(default_factory=dict)
    error: Optional[str] = None


@dataclass
class CrawlResult:
    """Result of web crawling."""

    success: bool
    crawl_id: Optional[str] = None
    status: str = "pending"
    pages: list[dict] = field(default_factory=list)
    error: Optional[str] = None


@dataclass
class SearchResult:
    """Result of web search."""

    success: bool
    results: list[dict] = field(default_factory=list)
    error: Optional[str] = None


class WebScraper:
    """Web scraping for content research and competitor analysis."""

    def __init__(self, api_client: "ApiClient") -> None:
        self.api_client = api_client

    def scrape(
        self,
        url: str,
        formats: list[str] = None,
        only_main_content: bool = True,
    ) -> ScrapeResult:
        """Scrape a URL for content.

        Args:
            url: URL to scrape
            formats: Formats to return (markdown, html, links)
            only_main_content: Whether to extract only main content

        Returns:
            ScrapeResult with scraped content
        """
        Logger.info("Scraping URL", {"url": url})

        formats = formats or ["markdown"]

        try:
            response = self.api_client.post(
                "/skills/web/scrape",
                {
                    "url": url,
                    "formats": formats,
                    "onlyMainContent": only_main_content,
                },
            )

            if response.success and response.data:
                data = response.data
                return ScrapeResult(
                    success=True,
                    markdown=data.get("markdown", ""),
                    html=data.get("html", ""),
                    links=data.get("links", {}).get("internal", [])
                    + data.get("links", {}).get("external", []),
                    metadata=data.get("metadata", {}),
                )

            return ScrapeResult(
                success=False,
                error=response.error or "Unknown error scraping",
            )

        except Exception as e:
            Logger.error("Failed to scrape URL", e)
            return ScrapeResult(success=False, error=str(e))

    def extract_structured(
        self,
        url: str,
        schema: dict,
        prompt: str,
    ) -> ExtractResult:
        """Extract structured data from a URL using LLM.

        Args:
            url: URL to extract from
            schema: JSON schema for extraction
            prompt: Prompt to guide extraction

        Returns:
            ExtractResult with extracted data
        """
        Logger.info("Extracting structured data", {"url": url})

        try:
            response = self.api_client.post(
                "/skills/web/extract",
                {
                    "url": url,
                    "schema": schema,
                    "prompt": prompt,
                },
            )

            if response.success and response.data:
                return ExtractResult(
                    success=True,
                    data=response.data,
                )

            return ExtractResult(
                success=False,
                error=response.error or "Unknown error extracting",
            )

        except Exception as e:
            Logger.error("Failed to extract data", e)
            return ExtractResult(success=False, error=str(e))

    def start_crawl(
        self,
        url: str,
        max_depth: int = 2,
        limit: int = 10,
        include_paths: list[str] = None,
        exclude_paths: list[str] = None,
    ) -> CrawlResult:
        """Start crawling a website.

        Args:
            url: Starting URL
            max_depth: Maximum crawl depth
            limit: Maximum pages to crawl
            include_paths: Paths to include
            exclude_paths: Paths to exclude

        Returns:
            CrawlResult with crawl ID
        """
        Logger.info("Starting crawl", {"url": url, "depth": max_depth})

        try:
            payload = {
                "url": url,
                "maxDepth": max_depth,
                "limit": limit,
                "scrapeOptions": {
                    "formats": ["markdown"],
                    "onlyMainContent": True,
                },
            }

            if include_paths:
                payload["includePaths"] = include_paths
            if exclude_paths:
                payload["excludePaths"] = exclude_paths

            response = self.api_client.post(
                "/skills/web/crawl",
                payload,
            )

            if response.success and response.data:
                data = response.data
                return CrawlResult(
                    success=True,
                    crawl_id=data.get("crawlId") or data.get("id"),
                    status="started",
                )

            return CrawlResult(
                success=False,
                error=response.error or "Unknown error starting crawl",
            )

        except Exception as e:
            Logger.error("Failed to start crawl", e)
            return CrawlResult(success=False, error=str(e))

    def get_crawl_status(self, crawl_id: str) -> CrawlResult:
        """Get crawl status.

        Args:
            crawl_id: The crawl ID

        Returns:
            CrawlResult with status and pages
        """
        try:
            response = self.api_client.get(f"/skills/web/crawl/{crawl_id}")

            if response.success and response.data:
                data = response.data
                return CrawlResult(
                    success=True,
                    crawl_id=crawl_id,
                    status=data.get("status", "unknown"),
                    pages=data.get("pages", []),
                )

            return CrawlResult(
                success=False,
                crawl_id=crawl_id,
                error=response.error or "Unknown error getting crawl status",
            )

        except Exception as e:
            Logger.error("Failed to get crawl status", e)
            return CrawlResult(success=False, crawl_id=crawl_id, error=str(e))

    def search(
        self,
        query: str,
        limit: int = 5,
        lang: str = "en",
    ) -> SearchResult:
        """Search the web and scrape results.

        Args:
            query: Search query
            limit: Number of results
            lang: Language code

        Returns:
            SearchResult with search results
        """
        Logger.info("Searching web", {"query": query})

        try:
            response = self.api_client.post(
                "/skills/web/search",
                {
                    "query": query,
                    "limit": limit,
                    "lang": lang,
                },
            )

            if response.success and response.data:
                return SearchResult(
                    success=True,
                    results=response.data.get("results", []),
                )

            return SearchResult(
                success=False,
                error=response.error or "Unknown error searching",
            )

        except Exception as e:
            Logger.error("Failed to search", e)
            return SearchResult(success=False, error=str(e))

    def format_scrape_result(self, result: ScrapeResult) -> str:
        """Format scrape result for display."""
        if not result.success:
            return f"❌ Scraping failed: {result.error}"

        lines = ["✅ Content scraped successfully", ""]

        if result.metadata.get("title"):
            lines.append(f"Title: {result.metadata['title']}")

        if result.markdown:
            preview = result.markdown[:800] + "..." if len(result.markdown) > 800 else result.markdown
            lines.extend([
                "",
                "Content preview:",
                "```markdown",
                preview,
                "```",
            ])

        if result.links:
            lines.extend([
                "",
                f"Found {len(result.links)} links",
            ])

        return "\n".join(lines)

    def format_search_result(self, result: SearchResult) -> str:
        """Format search result for display."""
        if not result.success:
            return f"❌ Search failed: {result.error}"

        lines = [f"🔍 Found {len(result.results)} results:", ""]

        for i, item in enumerate(result.results, 1):
            lines.append(f"{i}. **{item.get('title', 'No title')}**")
            lines.append(f"   {item.get('url', '')}")
            if item.get('description'):
                lines.append(f"   {item['description'][:150]}...")
            lines.append("")

        return "\n".join(lines)
