# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""
AI Assistant for Private Browser.

Provides AI-powered features:
- Page summarization
- Question answering about page content
- Data extraction
- Form filling assistance
"""

import logging
import re
from typing import Any, Dict, List, Optional

logger = logging.getLogger(__name__)


class BrowserAI:
    """
    AI assistant for the private browser.

    Uses the Familiar agent's LLM to provide intelligent
    assistance while browsing.
    """

    def __init__(self, agent):
        self.agent = agent

    async def summarize(
        self,
        content: str,
        title: str,
        max_length: int = 200,
    ) -> str:
        """
        Summarize page content.

        Args:
            content: Page text content
            title: Page title
            max_length: Maximum summary length in words

        Returns:
            Summary text
        """
        if not self.agent:
            return self._fallback_summarize(content)

        # Truncate content if too long
        content = content[:10000]

        prompt = f"""Summarize this web page in {max_length} words or less.

Title: {title}

Content:
{content}

Summary:"""

        try:
            response = await self.agent.llm.generate(
                prompt,
                max_tokens=max_length * 2,
                temperature=0.3,
            )
            return response.strip()
        except Exception as e:
            logger.error(f"AI summarization error: {e}")
            return self._fallback_summarize(content)

    def _fallback_summarize(self, content: str) -> str:
        """Simple extractive summary as fallback."""
        # Get first few sentences
        sentences = re.split(r"[.!?]+", content)
        sentences = [s.strip() for s in sentences if len(s.strip()) > 30]

        summary = ". ".join(sentences[:3])
        if summary and not summary.endswith("."):
            summary += "."

        return summary[:500] if summary else "Unable to generate summary."

    async def answer_question(
        self,
        question: str,
        content: str,
        title: str,
    ) -> str:
        """
        Answer a question about the page.

        Args:
            question: User's question
            content: Page text content
            title: Page title

        Returns:
            Answer text
        """
        if not self.agent:
            return "AI assistant not available. Please configure an LLM provider."

        # Truncate content if too long
        content = content[:15000]

        prompt = f"""Based on the following web page content, answer the question.
If the answer is not found in the content, say so.

Page Title: {title}

Page Content:
{content}

Question: {question}

Answer:"""

        try:
            response = await self.agent.llm.generate(
                prompt,
                max_tokens=500,
                temperature=0.3,
            )
            return response.strip()
        except Exception as e:
            logger.error(f"AI question answering error: {e}")
            return f"Error: Unable to answer question. {str(e)}"

    async def extract_data(
        self,
        content: str,
        title: str,
        data_type: str = "auto",
    ) -> Dict[str, Any]:
        """
        Extract structured data from a page.

        Args:
            content: Page text content
            title: Page title
            data_type: Type of data to extract
                       (auto, prices, contacts, events, products, etc.)

        Returns:
            Extracted data dictionary
        """
        if not self.agent:
            return self._fallback_extract(content, data_type)

        # Truncate content
        content = content[:10000]

        if data_type == "auto":
            type_prompt = (
                "Identify what type of content this is and extract relevant structured data."
            )
        else:
            type_prompts = {
                "prices": "Extract all prices, product names, and any discounts or sales.",
                "contacts": "Extract contact information: names, emails, phone numbers, addresses.",
                "events": "Extract event details: names, dates, times, locations, descriptions.",
                "products": "Extract product information: names, prices, descriptions, ratings.",
                "recipes": "Extract recipe details: title, ingredients, instructions, prep time.",
                "articles": "Extract article metadata: title, author, date, main topics.",
            }
            type_prompt = type_prompts.get(data_type, f"Extract {data_type} data.")

        prompt = f"""Analyze this web page and extract structured data as JSON.

{type_prompt}

Page Title: {title}

Content:
{content}

Return ONLY valid JSON with the extracted data. No explanation, just JSON:"""

        try:
            response = await self.agent.llm.generate(
                prompt,
                max_tokens=1000,
                temperature=0.1,
            )

            # Try to parse JSON from response
            import json

            # Find JSON in response
            json_match = re.search(r"\{[\s\S]*\}", response)
            if json_match:
                return json.loads(json_match.group())

            return {"raw": response, "error": "Could not parse JSON"}

        except Exception as e:
            logger.error(f"AI data extraction error: {e}")
            return self._fallback_extract(content, data_type)

    def _fallback_extract(
        self,
        content: str,
        data_type: str,
    ) -> Dict[str, Any]:
        """Simple pattern-based extraction as fallback."""
        result = {"type": data_type}

        # Extract emails
        emails = re.findall(r"[\w.-]+@[\w.-]+\.\w+", content)
        if emails:
            result["emails"] = list(set(emails))

        # Extract phone numbers
        phones = re.findall(r"\b\d{3}[-.]?\d{3}[-.]?\d{4}\b", content)
        if phones:
            result["phones"] = list(set(phones))

        # Extract prices
        prices = re.findall(r"\$[\d,]+\.?\d*", content)
        if prices:
            result["prices"] = list(set(prices))

        # Extract dates
        dates = re.findall(
            r"\b(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\.?\s+\d{1,2},?\s+\d{4}\b",
            content,
            re.IGNORECASE,
        )
        if dates:
            result["dates"] = list(set(dates))

        # Extract URLs
        urls = re.findall(r'https?://[^\s<>"]+', content)
        if urls:
            result["urls"] = list(set(urls))[:20]  # Limit

        return result

    async def suggest_actions(
        self,
        content: str,
        title: str,
    ) -> List[Dict[str, str]]:
        """
        Suggest actions the user might want to take.

        Args:
            content: Page text content
            title: Page title

        Returns:
            List of suggested actions
        """
        if not self.agent:
            return self._fallback_suggest(content, title)

        content = content[:5000]

        prompt = f"""Based on this web page, suggest 3-5 actions the user might want to take.
Return as JSON array with 'action' and 'description' fields.

Page: {title}

Content:
{content[:3000]}

Suggestions (JSON array only):"""

        try:
            response = await self.agent.llm.generate(
                prompt,
                max_tokens=500,
                temperature=0.5,
            )

            import json

            json_match = re.search(r"\[[\s\S]*\]", response)
            if json_match:
                return json.loads(json_match.group())

        except Exception as e:
            logger.error(f"AI suggestion error: {e}")

        return self._fallback_suggest(content, title)

    def _fallback_suggest(
        self,
        content: str,
        title: str,
    ) -> List[Dict[str, str]]:
        """Generate basic suggestions without AI."""
        suggestions = []

        # Check for common page types
        content_lower = content.lower()

        if any(word in content_lower for word in ["price", "buy", "add to cart", "shop"]):
            suggestions.append(
                {"action": "Track price", "description": "Monitor this product for price drops"}
            )

        if any(word in content_lower for word in ["article", "news", "story", "published"]):
            suggestions.append(
                {"action": "Save to archive", "description": "Save this article for later reading"}
            )
            suggestions.append(
                {"action": "Get summary", "description": "Generate a quick summary of this article"}
            )

        if any(word in content_lower for word in ["recipe", "ingredients", "instructions"]):
            suggestions.append(
                {"action": "Save recipe", "description": "Extract and save this recipe"}
            )

        if "@" in content or "contact" in content_lower:
            suggestions.append(
                {
                    "action": "Extract contacts",
                    "description": "Get email addresses and phone numbers",
                }
            )

        # Always suggest these
        suggestions.append({"action": "Reader mode", "description": "View in clean reader format"})

        return suggestions[:5]

    async def compare_pages(
        self,
        pages: List[Dict[str, str]],
    ) -> str:
        """
        Compare multiple pages (e.g., product comparison).

        Args:
            pages: List of {'title': str, 'content': str}

        Returns:
            Comparison text
        """
        if not self.agent or len(pages) < 2:
            return "Need at least 2 pages and AI to compare."

        # Build comparison prompt
        page_texts = []
        for i, page in enumerate(pages[:5]):  # Limit to 5 pages
            page_texts.append(f"Page {i + 1}: {page['title']}\n{page['content'][:2000]}")

        prompt = f"""Compare these web pages and highlight key differences and similarities.

{chr(10).join(page_texts)}

Comparison:"""

        try:
            response = await self.agent.llm.generate(
                prompt,
                max_tokens=800,
                temperature=0.3,
            )
            return response.strip()
        except Exception as e:
            logger.error(f"AI comparison error: {e}")
            return f"Error comparing pages: {str(e)}"

    async def generate_form_data(
        self,
        form_fields: List[str],
        context: Optional[str] = None,
    ) -> Dict[str, str]:
        """
        Generate data to fill a form.

        Args:
            form_fields: List of form field names
            context: Optional context about what the form is for

        Returns:
            Dictionary of field names to values
        """
        if not self.agent:
            return {}

        # Get user profile if available
        profile = {}
        if hasattr(self.agent, "memory") and self.agent.memory:
            profile = self.agent.memory.get("user_profile", {})

        prompt = f"""Generate appropriate values for these form fields.
Use the user profile if available, otherwise generate reasonable placeholder values.

User Profile: {profile}
Context: {context or "General form"}
Fields: {", ".join(form_fields)}

Return as JSON with field names as keys:"""

        try:
            response = await self.agent.llm.generate(
                prompt,
                max_tokens=500,
                temperature=0.3,
            )

            import json

            json_match = re.search(r"\{[\s\S]*\}", response)
            if json_match:
                return json.loads(json_match.group())

        except Exception as e:
            logger.error(f"AI form generation error: {e}")

        return {}
