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

This client allows you to programmatically interact with the Local Deep Research (LDR)
application, enabling seamless integration with Python scripts and applications.
It handles all the complexity of authentication, session management, and request formatting.

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.

Features:
---------
- Automatic login and session management
- CSRF token handling
- Research query submission and result retrieval
- User settings management
- Research history access

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"])

    # With context manager (auto-logout)
    with LDRClient() as client:
        client.login("username", "password")
        result = client.quick_research("What is quantum computing?")
        print(result["summary"])

    # Get research history
    with LDRClient() as client:
        client.login("username", "password")
        history = client.get_history()
        for item in history:
            print(f"Research: {item['query']}")

    # One-liner for quick queries
    from local_deep_research.api.client import quick_query
    summary = quick_query("username", "password", "What is DNA?")

    # Update user settings
    with LDRClient() as client:
        client.login("username", "password")
        client.update_setting("llm.model", "gemma:7b")
        settings = client.get_settings()
        print(f"Current model: {settings['llm']['model']}")
"""

import requests
import time
from typing import Optional, Dict, Any, List
from loguru import logger
from local_deep_research.benchmarks.comparison.results import Benchmark_results


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]:
        """
        Research a topic using LLMs and search engines.

        This method runs a research process on your query using search engines
        and large language models. It might take a few minutes to complete.

        Args:
            query: Your research question
            model: LLM model to use (e.g., "gemma:7b", "llama2:7b")
            search_engines: Search engines to use (default: ["searxng"])
            iterations: How many research cycles to run (default: 2)
            wait_for_result: If True, wait until done. If False, return immediately
            timeout: Maximum seconds to wait (default: 300)

        Returns:
            If waiting for result: Dict with summary, sources, and findings
            If not waiting: Dict with research_id to check status later

        Raises:
            RuntimeError: If not logged in or request fails

        Example:
            result = client.quick_research("Latest developments in fusion energy")
            print(result["summary"])
        """
        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 get results.

        Use this after starting research with quick_research(wait_for_result=False).
        Checks status every 5 seconds until complete or timeout.

        Args:
            research_id: ID of the research to wait for
            timeout: Maximum seconds to wait (default: 300)

        Returns:
            Dict with research results (summary, sources, findings)

        Raises:
            RuntimeError: If research fails or times out

        Example:
            # Start research without waiting
            resp = client.quick_research("Climate change impacts", wait_for_result=False)
            # Get results when ready
            results = client.wait_for_research(resp["research_id"])
        """
        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 your past research queries.

        Returns a list of previous research sessions with their details.

        Returns:
            List of research items with query, timestamp, and status info

        Raises:
            RuntimeError: If not logged in

        Example:
            history = client.get_history()
            for item in history[:5]:
                print(f"{item['timestamp']}: {item['query']}")
        """
        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 submit_benchmark(
        self,
        model,
        hardware,
        accuracy_focused,
        accuracy_source,
        avg_time_per_question,
        context_window,
        temperature,
        ldr_version,
        date_tested,
        notes="",
    ):
        """
        Submit your benchmark results to help the community.

        Args:
            model: Model name (e.g., "Llama-3.3-70B-Q4_K_M")
            hardware: Hardware specs (e.g., "RTX 4090 24GB")
            accuracy_focused: Accuracy percentage for focused strategy
            accuracy_source: Accuracy percentage for source-based strategy
            avg_time_per_question: Average time per question in seconds
            context_window: Context window size used
            temperature: Temperature setting used
            ldr_version: Version of LDR used (e.g., "0.6.0")
            date_tested: Date tested (YYYY-MM-DD format)
            notes: Optional notes about the test

        Returns:
            True if submission was successful

        Example:
            client.submit_benchmark(
                "Llama-3.3-70B-Q4_K_M", "RTX 4090 24GB",
                87.0, 82.0, 45.2, 32000, 0.1, "0.6.0", "2024-01-15"
            )
        """
        benchmarks = Benchmark_results()
        return benchmarks.add_result(
            model,
            hardware,
            accuracy_focused,
            accuracy_source,
            avg_time_per_question,
            context_window,
            temperature,
            ldr_version,
            date_tested,
            notes,
        )

    def get_benchmarks(self, best_only=False):
        """
        Get community benchmark results.

        Args:
            best_only: If True, only return top performers

        Returns:
            List of benchmark results

        Example:
            all_results = client.get_benchmarks()
            top_results = client.get_benchmarks(best_only=True)
        """
        benchmarks = Benchmark_results()
        if best_only:
            return benchmarks.get_best()
        return benchmarks.get_all()

    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")
