"""
HTTP Client for Local Deep Research API.
Simplifies authentication and API access by handling CSRF tokens automatically.

Why CSRF with login?
--------------------
CSRF tokens prevent cross-site request forgery attacks. Even though you're logged in,
CSRF ensures requests come from YOUR code, not from malicious websites that might
try to use your browser's active session cookies to make unauthorized requests.

Example usage:
-------------
    from local_deep_research.api.client import LDRClient

    # Simple usage
    client = LDRClient()
    client.login("username", "password")
    result = client.quick_research("What is quantum computing?")
    print(result["summary"])

    # Or use the one-liner
    from local_deep_research.api.client import quick_query
    summary = quick_query("username", "password", "What is DNA?")
"""

import requests
import time
from typing import Optional, Dict, Any, List
from loguru import logger


class LDRClient:
    """
    HTTP client for LDR API access with automatic CSRF handling.

    This client abstracts away the complexity of:
    - Extracting CSRF tokens from HTML forms
    - Managing session cookies
    - Handling authentication flow
    - Polling for research results
    """

    def __init__(self, base_url: str = "http://localhost:5000"):
        """
        Initialize the client.

        Args:
            base_url: URL of the LDR server (default: http://localhost:5000)
        """
        self.base_url = base_url
        self.session = requests.Session()
        self.csrf_token = None
        self.logged_in = False
        self.username = None

    def login(self, username: str, password: str) -> bool:
        """
        Login to LDR server. Handles all CSRF complexity internally.

        This method:
        1. Gets the login page to extract CSRF token from HTML form
        2. Submits login with form data (not JSON)
        3. Retrieves CSRF token for subsequent API calls

        Args:
            username: Your LDR username
            password: Your LDR password

        Returns:
            True if login successful, False otherwise
        """
        try:
            # Step 1: Get login page to extract CSRF token
            # We need to parse HTML because Flask-WTF embeds CSRF in forms
            login_page = self.session.get(f"{self.base_url}/auth/login")

            # Simple CSRF extraction without BeautifulSoup dependency
            # Look for: <input type="hidden" name="csrf_token" value="..."/>
            import re

            csrf_match = re.search(
                r'<input[^>]*name="csrf_token"[^>]*value="([^"]*)"',
                login_page.text,
            )

            if not csrf_match:
                logger.error("Could not find CSRF token in login page")
                return False

            login_csrf = csrf_match.group(1)

            # Step 2: Login with form data (NOT JSON!)
            # Flask-WTF expects form-encoded data for login
            response = self.session.post(
                f"{self.base_url}/auth/login",
                data={
                    "username": username,
                    "password": password,
                    "csrf_token": login_csrf,
                },
                allow_redirects=True,
            )

            if response.status_code not in [200, 302]:
                logger.error(
                    f"Login failed with status: {response.status_code}"
                )
                return False

            # Step 3: Get CSRF token for API requests
            # This uses our new endpoint that returns JSON
            csrf_response = self.session.get(f"{self.base_url}/auth/csrf-token")
            if csrf_response.status_code == 200:
                self.csrf_token = csrf_response.json()["csrf_token"]
                self.logged_in = True
                self.username = username
                logger.info(f"Successfully logged in as {username}")
                return True
            else:
                logger.warning("Logged in but could not get API CSRF token")
                # Still logged in, just no CSRF for API calls
                self.logged_in = True
                self.username = username
                return True

        except Exception:
            logger.exception("Login error")
            return False

    def _api_headers(self) -> Dict[str, str]:
        """Get headers with CSRF token for API requests."""
        if self.csrf_token:
            return {"X-CSRF-Token": self.csrf_token}
        return {}

    def quick_research(
        self,
        query: str,
        model: Optional[str] = None,
        search_engines: Optional[List[str]] = None,
        iterations: int = 2,
        wait_for_result: bool = True,
        timeout: int = 300,
    ) -> Dict[str, Any]:
        """
        Perform research on a topic.

        Args:
            query: Research question
            model: LLM model to use (optional, uses user's default if not specified)
            search_engines: List of search engines (default: ["searxng"])
            iterations: Number of research iterations (default: 2)
            wait_for_result: If True, wait for completion. If False, return immediately with research_id
            timeout: Maximum time to wait in seconds (default: 300)

        Returns:
            If wait_for_result=True: Dict with summary, sources, findings
            If wait_for_result=False: Dict with research_id for polling manually

        Raises:
            RuntimeError: If not logged in or request fails
        """
        if not self.logged_in:
            raise RuntimeError("Not logged in. Call login() first.")

        # Default search engines
        if search_engines is None:
            search_engines = ["searxng"]

        # Start research
        response = self.session.post(
            f"{self.base_url}/research/api/start",
            json={
                "query": query,
                "model": model,
                "search_engines": search_engines,
                "iterations": iterations,
                "questions_per_iteration": 3,
            },
            headers=self._api_headers(),
        )

        # Handle response
        if response.status_code != 200:
            # Try to extract error message
            try:
                error_data = response.json()
                if isinstance(error_data, list) and len(error_data) > 0:
                    error_msg = error_data[0].get("message", "Unknown error")
                else:
                    error_msg = str(error_data)
            except:
                error_msg = response.text[:200]
            raise RuntimeError(f"Failed to start research: {error_msg}")

        result = response.json()
        research_id = result.get("research_id")

        if not research_id:
            raise RuntimeError("No research ID returned")

        if not wait_for_result:
            return {"research_id": research_id}

        # Poll for results
        return self.wait_for_research(research_id, timeout)

    def wait_for_research(
        self, research_id: str, timeout: int = 300
    ) -> Dict[str, Any]:
        """
        Wait for research to complete and return results.

        Args:
            research_id: ID of the research to wait for
            timeout: Maximum time to wait in seconds

        Returns:
            Dict with research results

        Raises:
            RuntimeError: If research fails or times out
        """
        start_time = time.time()

        while time.time() - start_time < timeout:
            status_response = self.session.get(
                f"{self.base_url}/research/api/research/{research_id}/status"
            )

            if status_response.status_code == 200:
                status = status_response.json()

                if status.get("status") == "completed":
                    # Get final results
                    results_response = self.session.get(
                        f"{self.base_url}/research/api/research/{research_id}/result"
                    )
                    if results_response.status_code == 200:
                        return results_response.json()
                    else:
                        raise RuntimeError("Failed to get results")

                elif status.get("status") == "failed":
                    error_msg = status.get("error", "Unknown error")
                    raise RuntimeError(f"Research failed: {error_msg}")

            time.sleep(5)

        raise RuntimeError(f"Research timed out after {timeout} seconds")

    def get_settings(self) -> Dict[str, Any]:
        """Get current user settings."""
        if not self.logged_in:
            raise RuntimeError("Not logged in. Call login() first.")

        response = self.session.get(f"{self.base_url}/settings/api")
        if response.status_code == 200:
            return response.json()
        else:
            raise RuntimeError(
                f"Failed to get settings: {response.status_code}"
            )

    def update_setting(self, key: str, value: Any) -> bool:
        """
        Update a setting.

        Args:
            key: Setting key (e.g., "llm.model")
            value: New value for the setting

        Returns:
            True if successful
        """
        if not self.logged_in:
            raise RuntimeError("Not logged in. Call login() first.")

        response = self.session.put(
            f"{self.base_url}/settings/api/{key}",
            json={"value": value},
            headers=self._api_headers(),
        )
        return response.status_code == 200

    def get_history(self) -> List[Dict[str, Any]]:
        """Get research history."""
        if not self.logged_in:
            raise RuntimeError("Not logged in. Call login() first.")

        response = self.session.get(f"{self.base_url}/history/api")
        if response.status_code == 200:
            data = response.json()
            # Handle different response formats
            if isinstance(data, dict):
                return data.get("history", data.get("items", []))
            elif isinstance(data, list):
                return data
            return []
        else:
            raise RuntimeError(f"Failed to get history: {response.status_code}")

    def logout(self):
        """Logout and clear session."""
        if self.logged_in:
            self.session.post(
                f"{self.base_url}/auth/logout", headers=self._api_headers()
            )
        self.session.close()
        self.csrf_token = None
        self.logged_in = False
        self.username = None

    def __enter__(self):
        """Support context manager for auto-cleanup."""
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        """Auto logout when used as context manager."""
        self.logout()


# Convenience functions for simple use cases


def quick_query(
    username: str,
    password: str,
    query: str,
    base_url: str = "http://localhost:5000",
) -> str:
    """
    One-liner for quick research queries.

    Example:
        summary = quick_query("user", "pass", "What is DNA?")
        print(summary)

    Args:
        username: LDR username
        password: LDR password
        query: Research question
        base_url: Server URL

    Returns:
        Research summary as string
    """
    with LDRClient(base_url) as client:
        if not client.login(username, password):
            raise RuntimeError("Login failed")

        result = client.quick_research(query)
        return result.get("summary", "No summary available")
