"""TrustMemClient — Thin HTTP client for TrustMem server.

Usage::

    from trustmem_client import TrustMemClient

    # Set in .env (recommended)
    # TRUSTMEM_BASE_URL=http://localhost:8080
    # TRUSTMEM_API_KEY=tm-xxx

    # Auto-loaded from environment variables
    client = TrustMemClient()

    # Or specify explicitly (overrides environment variables)
    client = TrustMemClient(base_url="https://example.com", api_key="tm-xxx")

    client.add([{"role": "user", "content": "Alex likes sushi"}], user_id="alex")
    results = client.search("food preferences", user_id="alex")
    print(results)
"""
from __future__ import annotations

import os
from typing import Any, Optional

import httpx


class TrustMemClient:
    """HTTP client for TrustMem server.

    Provides the same core CRUD interface as TrustMem (embedded),
    but routes all operations over HTTP to a running TrustMem server.

    Configuration is loaded from (in order of priority):
    1. Constructor arguments (explicit)
    2. Environment variables / .env file (fallback)

    Environment variables:
    - ``TRUSTMEM_BASE_URL``: Server URL (default: http://localhost:8080)
    - ``TRUSTMEM_API_KEY``: API key for Bearer authentication
    """

    def __init__(
        self,
        base_url: Optional[str] = None,
        api_key: Optional[str] = None,
        timeout: float = 30.0,
    ) -> None:
        try:
            from dotenv import load_dotenv
            load_dotenv(override=False)
        except ImportError:
            pass

        resolved_base_url = base_url or os.getenv(
            "TRUSTMEM_BASE_URL", "http://localhost:8080"
        )
        resolved_api_key = api_key if api_key is not None else os.getenv(
            "TRUSTMEM_API_KEY"
        )

        headers: dict[str, str] = {}
        if resolved_api_key:
            headers["Authorization"] = f"Bearer {resolved_api_key}"
        self._client = httpx.Client(
            base_url=resolved_base_url.rstrip("/"),
            headers=headers,
            timeout=timeout,
        )

    # ------------------------------------------------------------------
    # Core CRUD (4 methods)
    # ------------------------------------------------------------------

    def add(
        self,
        data: list[dict],
        *,
        user_id: Optional[str] = None,
        visibility: Optional[str] = None,
        memory_type: str = "semantic",
        tags: Optional[list] = None,
        metadata: Optional[dict] = None,
        session_id: Optional[str] = None,
        dry_run: bool = False,
        source_trust: Optional[str] = None,
        expires_at: Optional[str] = None,
    ) -> dict:
        """Extract and store memories from conversation messages.

        Args:
            user_id: User scope. Mutually exclusive with visibility.
            visibility: ``"organization"`` for org-shared memories.
                Mutually exclusive with user_id.
            source_trust: Trust level of the data source.
                ``"verified"`` — system-integrated data (CRM, API, DB).
                ``"input"`` — authenticated user's direct statements.
                ``"derived"`` — AI-generated/inferred (default).
        """
        body: dict[str, Any] = {
            "data": data,
            "memory_type": memory_type,
        }
        if user_id is not None:
            body["user_id"] = user_id
        if visibility is not None:
            body["visibility"] = visibility
        if tags is not None:
            body["tags"] = tags
        if metadata is not None:
            body["metadata"] = metadata
        if session_id is not None:
            body["session_id"] = session_id
        if dry_run:
            body["dry_run"] = True
        if source_trust is not None:
            body["source_trust"] = source_trust
        if expires_at is not None:
            body["expires_at"] = expires_at
        resp = self._client.post("/v1/memories", json=body)
        resp.raise_for_status()
        return resp.json()

    def search(
        self,
        query: str,
        *,
        user_id: Optional[str] = None,
        visibility: Optional[str] = None,
        limit: int = 10,
        min_source_trust: Optional[str] = None,
        filters: Optional[dict] = None,
        accessor: Optional[str] = None,
        exclude_expired: bool = False,
        memory_type: Optional[str] = None,
        search_mode: Optional[str] = None,
    ) -> dict:
        """Semantic search for memories.

        Args:
            user_id: User scope. Mutually exclusive with visibility.
            visibility: ``"organization"`` for org-shared memories.
                Mutually exclusive with user_id.
            min_source_trust: Minimum source trust filter.
                ``"verified"`` — only verified memories.
                ``"input"`` — verified + input.
                ``None`` — all (default).
            filters: Metadata pre-filters.
                Example: ``{"source": "salesforce"}`` returns only memories
                whose metadata contains ``source == "salesforce"``.
            accessor: Accessor name for Read Audit logging.
        """
        body: dict[str, Any] = {
            "query": query,
            "limit": limit,
        }
        if user_id is not None:
            body["user_id"] = user_id
        if visibility is not None:
            body["visibility"] = visibility
        if min_source_trust is not None:
            body["min_source_trust"] = min_source_trust
        if filters is not None:
            body["filters"] = filters
        if exclude_expired:
            body["exclude_expired"] = True
        if memory_type is not None:
            body["memory_type"] = memory_type
        if search_mode is not None:
            body["search_mode"] = search_mode
        headers: dict[str, str] = {}
        if accessor is not None:
            headers["X-TrustMem-Accessor"] = accessor
        resp = self._client.post("/v1/memories/search", json=body, headers=headers)
        resp.raise_for_status()
        return resp.json()

    def search_raw(
        self,
        query: str,
        *,
        user_id: Optional[str] = None,
        visibility: Optional[str] = None,
        limit: int = 10,
        filters: Optional[dict] = None,
        accessor: Optional[str] = None,
        exclude_expired: bool = False,
        memory_type: Optional[str] = None,
    ) -> dict:
        """Pure cosine similarity search without TrustMem ranking.

        Results are ranked solely by cosine similarity.
        No graph relations are included.

        Args:
            user_id: User scope. Mutually exclusive with visibility.
            visibility: ``"organization"`` for org-shared memories.
                Mutually exclusive with user_id.
            filters: Metadata pre-filters.
                Example: ``{"source": "salesforce"}`` returns only memories
                whose metadata contains ``source == "salesforce"``.
            accessor: Accessor name for Read Audit logging.
        """
        body: dict[str, Any] = {
            "query": query,
            "limit": limit,
        }
        if user_id is not None:
            body["user_id"] = user_id
        if visibility is not None:
            body["visibility"] = visibility
        if filters is not None:
            body["filters"] = filters
        if exclude_expired:
            body["exclude_expired"] = True
        if memory_type is not None:
            body["memory_type"] = memory_type
        headers: dict[str, str] = {}
        if accessor is not None:
            headers["X-TrustMem-Accessor"] = accessor
        resp = self._client.post("/v1/memories/search-raw", json=body, headers=headers)
        resp.raise_for_status()
        return resp.json()

    def get_all(
        self,
        *,
        user_id: Optional[str] = None,
        visibility: Optional[str] = None,
        limit: int = 100,
        offset: int = 0,
        relation_limit: int = 100,
        min_source_trust: Optional[str] = None,
        accessor: Optional[str] = None,
        memory_type: Optional[str] = None,
    ) -> dict:
        """Get all memories.

        Args:
            user_id: User scope. Mutually exclusive with visibility.
            visibility: ``"organization"`` for org-shared memories.
                Mutually exclusive with user_id.
            min_source_trust: Minimum source trust filter.
            accessor: Accessor name for Read Audit logging.
        """
        params: dict[str, Any] = {
            "limit": limit,
            "offset": offset,
            "relation_limit": relation_limit,
        }
        if user_id is not None:
            params["user_id"] = user_id
        if visibility is not None:
            params["visibility"] = visibility
        if min_source_trust is not None:
            params["min_source_trust"] = min_source_trust
        if memory_type is not None:
            params["memory_type"] = memory_type
        headers: dict[str, str] = {}
        if accessor is not None:
            headers["X-TrustMem-Accessor"] = accessor
        resp = self._client.get("/v1/memories", params=params, headers=headers)
        resp.raise_for_status()
        return resp.json()

    def delete(
        self,
        memory_id: str,
    ) -> bool:
        """Delete a memory by ID."""
        resp = self._client.delete(f"/v1/memories/{memory_id}")
        resp.raise_for_status()
        return resp.json().get("deleted", False)

    # ------------------------------------------------------------------
    # Resource management
    # ------------------------------------------------------------------

    def close(self) -> None:
        """Close the HTTP connection."""
        self._client.close()

    def __enter__(self) -> "TrustMemClient":
        return self

    def __exit__(self, *args: Any) -> None:
        self.close()

    def __repr__(self) -> str:
        return f"TrustMemClient(base_url={self._client.base_url!r})"
