"""Mock implementations of dependencies for testing and development."""

from dataclasses import dataclass, field
from typing import Any

# Sample data for mocks
SAMPLE_USERS = [
    {"id": "user-001", "email": "alice@example.com", "name": "Alice Smith", "is_active": True},
    {"id": "user-002", "email": "bob@example.com", "name": "Bob Johnson", "is_active": True},
    {"id": "user-003", "email": "carol@example.com", "name": "Carol Williams", "is_active": False},
    {"id": "user-004", "email": "david@example.com", "name": "David Brown", "is_active": True},
    {"id": "user-005", "email": "eve@example.com", "name": "Eve Davis", "is_active": True},
]

SAMPLE_ORDERS = [
    {"id": "ord-001", "user_id": "user-001", "status": "completed", "total_amount": 150.00, "created_at": "2024-01-15"},
    {"id": "ord-002", "user_id": "user-001", "status": "completed", "total_amount": 89.99, "created_at": "2024-01-20"},
    {"id": "ord-003", "user_id": "user-002", "status": "pending", "total_amount": 245.50, "created_at": "2024-01-22"},
    {"id": "ord-004", "user_id": "user-001", "status": "completed", "total_amount": 67.00, "created_at": "2024-02-01"},
    {"id": "ord-005", "user_id": "user-004", "status": "cancelled", "total_amount": 199.99, "created_at": "2024-02-05"},
    {"id": "ord-006", "user_id": "user-002", "status": "completed", "total_amount": 432.00, "created_at": "2024-02-10"},
]

SAMPLE_DOCUMENTS = [
    {
        "id": "doc-001",
        "title": "Getting Started Guide",
        "content": "Welcome to our platform! This guide will help you get started with the basic features. First, create an account and verify your email. Then, explore the dashboard to see your analytics and settings.",
        "category": "documentation",
        "metadata": {"author": "docs-team", "updated": "2024-01-10"},
    },
    {
        "id": "doc-002",
        "title": "API Reference",
        "content": "Our REST API allows you to integrate with external systems. All endpoints require authentication via Bearer token. Rate limits apply: 1000 requests per hour for standard plans, unlimited for enterprise.",
        "category": "documentation",
        "metadata": {"author": "api-team", "updated": "2024-02-01"},
    },
    {
        "id": "doc-003",
        "title": "Billing FAQ",
        "content": "Common billing questions: How do I update my payment method? Go to Settings > Billing > Payment Methods. How do I cancel? Contact support or use the cancellation form. Refunds are processed within 5-7 business days.",
        "category": "support",
        "metadata": {"author": "support-team", "updated": "2024-01-25"},
    },
    {
        "id": "doc-004",
        "title": "Security Best Practices",
        "content": "Keep your account secure: Enable two-factor authentication, use strong passwords, review connected applications regularly. Report suspicious activity immediately to security@example.com.",
        "category": "security",
        "metadata": {"author": "security-team", "updated": "2024-02-15"},
    },
    {
        "id": "doc-005",
        "title": "Troubleshooting Connection Issues",
        "content": "If you're experiencing connection problems: 1) Check your internet connection 2) Clear browser cache 3) Disable VPN temporarily 4) Try a different browser. If issues persist, contact support with your error code.",
        "category": "support",
        "metadata": {"author": "support-team", "updated": "2024-02-20"},
    },
]


@dataclass
class MockHttpResponse:
    """Mock HTTP response."""

    status_code: int = 200
    _json: dict[str, Any] | list[Any] = field(default_factory=dict)
    content: bytes = b""
    text: str = ""

    def json(self) -> dict[str, Any] | list[Any]:
        return self._json


@dataclass
class MockHttpClient:
    """Mock HTTP client that returns realistic sample data."""

    async def get(self, url: str, **kwargs: Any) -> MockHttpResponse:
        """Mock GET request."""
        # Web search mock
        if "search" in url and "api.search" in url:
            query = kwargs.get("params", {}).get("q", "")
            return MockHttpResponse(
                _json={
                    "results": [
                        {
                            "title": f"Search result for: {query}",
                            "url": "https://example.com/result1",
                            "snippet": f"This is a relevant result about {query}. It contains useful information.",
                        },
                        {
                            "title": f"More about {query}",
                            "url": "https://example.com/result2",
                            "snippet": f"Another perspective on {query} with different insights.",
                        },
                    ]
                }
            )

        # GitHub API mocks
        if "api.github.com" in url:
            if "/search/issues" in url:
                return MockHttpResponse(
                    _json={
                        "items": [
                            {
                                "number": 123,
                                "title": "Bug: Login not working",
                                "state": "open",
                                "labels": [{"name": "bug"}, {"name": "high-priority"}],
                                "html_url": "https://github.com/example/repo/issues/123",
                            },
                            {
                                "number": 124,
                                "title": "Feature request: Dark mode",
                                "state": "open",
                                "labels": [{"name": "enhancement"}],
                                "html_url": "https://github.com/example/repo/issues/124",
                            },
                        ]
                    }
                )
            if "/issues/" in url:
                return MockHttpResponse(
                    _json={
                        "number": 123,
                        "title": "Bug: Login not working",
                        "state": "open",
                        "user": {"login": "developer1"},
                        "created_at": "2024-01-15T10:00:00Z",
                        "labels": [{"name": "bug"}],
                        "body": "When trying to login, users see a 500 error. Steps to reproduce: 1) Go to /login 2) Enter credentials 3) Click submit",
                    }
                )
            if "/pulls" in url:
                return MockHttpResponse(
                    _json=[
                        {
                            "number": 45,
                            "title": "Fix authentication bug",
                            "state": "open",
                            "user": {"login": "developer2"},
                            "head": {"ref": "fix/auth-bug"},
                            "base": {"ref": "main"},
                        }
                    ]
                )
            if "/repos/" in url:
                return MockHttpResponse(
                    _json={
                        "full_name": "example/repo",
                        "description": "An example repository",
                        "stargazers_count": 1250,
                        "forks_count": 234,
                        "language": "Python",
                        "default_branch": "main",
                        "open_issues_count": 42,
                        "html_url": "https://github.com/example/repo",
                    }
                )

        # Slack API mocks
        if "slack.com/api" in url:
            if "conversations.history" in url:
                return MockHttpResponse(
                    _json={
                        "ok": True,
                        "messages": [
                            {"user": "U123", "text": "Has anyone seen the latest report?"},
                            {"user": "U456", "text": "Yes, I'll share it in the channel"},
                        ],
                    }
                )
            if "conversations.list" in url:
                return MockHttpResponse(
                    _json={
                        "ok": True,
                        "channels": [
                            {"name": "general", "num_members": 150, "purpose": {"value": "General discussion"}},
                            {"name": "engineering", "num_members": 45, "purpose": {"value": "Engineering team"}},
                        ],
                    }
                )
            if "search.messages" in url:
                return MockHttpResponse(
                    _json={
                        "ok": True,
                        "messages": {
                            "matches": [
                                {"channel": {"name": "general"}, "username": "alice", "text": "Relevant message here"},
                            ]
                        },
                    }
                )

        # Default response
        return MockHttpResponse(
            content=b"<html><body>Mock webpage content</body></html>",
            text="<html><body>Mock webpage content</body></html>",
        )

    async def post(self, url: str, **kwargs: Any) -> MockHttpResponse:
        """Mock POST request."""
        # Slack send message
        if "chat.postMessage" in url:
            return MockHttpResponse(_json={"ok": True, "ts": "1234567890.123456"})

        # Email send
        if "email-service" in url:
            return MockHttpResponse(status_code=200, _json={"status": "sent"})

        return MockHttpResponse(status_code=200, _json={"status": "ok"})


@dataclass
class MockDatabase:
    """Mock database with sample data."""

    users: list[dict[str, Any]] = field(default_factory=lambda: list(SAMPLE_USERS))
    orders: list[dict[str, Any]] = field(default_factory=lambda: list(SAMPLE_ORDERS))
    # John Pye invoice tracking
    johnpye_invoices: list[dict[str, Any]] = field(default_factory=list)
    johnpye_lot_items: list[dict[str, Any]] = field(default_factory=list)
    # Conversation and usage tracking
    conversation_messages: dict[str, dict[str, Any]] = field(default_factory=dict)
    usage_records: list[dict[str, Any]] = field(default_factory=list)
    _id_counter: int = field(default=0)

    def _next_id(self) -> str:
        """Generate a simple incrementing ID."""
        self._id_counter += 1
        return f"jp-{self._id_counter:06d}"

    def _parse_johnpye_invoice_insert(
        self, query_lower: str, args: tuple[Any, ...], new_id: str
    ) -> dict[str, Any]:
        """Parse INSERT statement for johnpye_invoices and map args to columns."""
        import re

        # Extract column names from INSERT statement (DOTALL to match across newlines)
        match = re.search(
            r"insert into johnpye_invoices\s*\((.*?)\)\s*values",
            query_lower,
            re.DOTALL,
        )
        if not match:
            # Fallback to old format
            return self._legacy_johnpye_invoice_insert(args, new_id)

        # Parse columns, handling newlines and extra whitespace
        columns_str = match.group(1)
        columns = [c.strip() for c in columns_str.replace("\n", "").split(",")]

        # Build invoice dict from columns and args
        invoice: dict[str, Any] = {"id": new_id}
        for i, col in enumerate(columns):
            if i < len(args):
                invoice[col] = args[i]

        return invoice

    def _legacy_johnpye_invoice_insert(
        self, args: tuple[Any, ...], new_id: str
    ) -> dict[str, Any]:
        """Legacy 15-arg INSERT format from add_invoice tool."""
        return {
            "id": new_id,
            "user_id": args[0],
            "invoice_number": args[1],
            "created_at": args[2],
            "auction_name": args[3],
            "auction_location": args[4] if len(args) > 4 else None,
            "viewing_datetime": args[5] if len(args) > 5 else None,
            "delivery_type": args[6] if len(args) > 6 else None,
            "delivery_method": args[7] if len(args) > 7 else None,
            "items_subtotal_pence": args[8] if len(args) > 8 else 0,
            "delivery_cost_pence": args[9] if len(args) > 9 else 0,
            "buyers_premium_pence": args[10] if len(args) > 10 else 0,
            "total_vat_pence": args[11] if len(args) > 11 else 0,
            "grand_total_pence": args[12] if len(args) > 12 else 0,
            "invoice_status": args[13] if len(args) > 13 else "new",
            "notes": args[14] if len(args) > 14 else None,
        }

    async def fetch_one(self, query: str, *args: Any) -> dict[str, Any] | None:
        """Fetch a single record."""
        query_lower = query.lower()

        # John Pye: INSERT with RETURNING id (used by sync_invoices_from_johnpye)
        if "insert into johnpye_invoices" in query_lower and "returning" in query_lower:
            new_id = self._next_id()
            # Parse column order from the INSERT statement
            invoice = self._parse_johnpye_invoice_insert(query_lower, args, new_id)
            self.johnpye_invoices.append(invoice)
            return {"id": new_id}

        # User statistics query
        if "count(*)" in query_lower and "orders" in query_lower:
            user_id = args[0] if args else None
            user_orders = [o for o in self.orders if o["user_id"] == user_id]
            total_spent = sum(o["total_amount"] for o in user_orders)
            return {
                "total_orders": len(user_orders),
                "total_spent": total_spent,
                "avg_order_value": total_spent / len(user_orders) if user_orders else 0,
            }

        # John Pye: Check if invoice exists
        if "from johnpye_invoices" in query_lower and "select 1" in query_lower:
            user_id, invoice_number = args[0], args[1]
            for inv in self.johnpye_invoices:
                if inv["user_id"] == user_id and inv["invoice_number"] == invoice_number:
                    return {"exists": 1}
            return None

        # John Pye: Get invoice by user_id and invoice_number
        if "from johnpye_invoices" in query_lower and "invoice_number" in query_lower:
            user_id, invoice_number = args[0], args[1]
            for inv in self.johnpye_invoices:
                if inv["user_id"] == user_id and inv["invoice_number"] == invoice_number:
                    return inv
            return None

        # John Pye: Get invoice by ID (for lot items lookup)
        if "from johnpye_invoices" in query_lower and len(args) == 2:
            user_id, invoice_number = args[0], args[1]
            for inv in self.johnpye_invoices:
                if inv["user_id"] == user_id and inv["invoice_number"] == invoice_number:
                    return inv
            return None

        # John Pye: Spending summary (aggregate query)
        if "from johnpye_invoices" in query_lower and "count(*)" in query_lower:
            user_id = args[0] if args else None
            user_invoices = [i for i in self.johnpye_invoices if i["user_id"] == user_id]
            return {
                "total_invoices": len(user_invoices),
                "total_spent": sum(i.get("grand_total_pence", 0) for i in user_invoices),
                "outstanding": sum(
                    i.get("grand_total_pence", 0)
                    for i in user_invoices
                    if i.get("invoice_status") == "new"
                ),
                "total_premium": sum(i.get("buyers_premium_pence", 0) for i in user_invoices),
                "total_delivery": sum(i.get("delivery_cost_pence", 0) for i in user_invoices),
                "total_vat": sum(i.get("total_vat_pence", 0) for i in user_invoices),
            }

        # John Pye: Count lot items
        if "from johnpye_lot_items" in query_lower and "count(*)" in query_lower:
            user_id = args[0] if args else None
            user_invoice_ids = {i["id"] for i in self.johnpye_invoices if i["user_id"] == user_id}
            total_items = sum(1 for li in self.johnpye_lot_items if li["invoice_id"] in user_invoice_ids)
            return {"total_items": total_items}

        # Conversation messages: SELECT messages WHERE conversation_id
        if "from conversation_messages" in query_lower and "select" in query_lower:
            conv_id = args[0] if args else None
            if conv_id and conv_id in self.conversation_messages:
                return {"messages": self.conversation_messages[conv_id].get("messages", [])}
            return None

        # Conversation messages: DELETE with RETURNING
        if "delete from conversation_messages" in query_lower:
            conv_id = args[0] if args else None
            if conv_id and conv_id in self.conversation_messages:
                del self.conversation_messages[conv_id]
                return {"conversation_id": conv_id}
            return None

        # Usage records: Aggregate queries
        if "from usage_records" in query_lower:
            user_id = args[0] if args else None
            user_records = [r for r in self.usage_records if r.get("user_id") == user_id]

            # Simple sum query (for check_limit)
            if "coalesce(sum(total_tokens)" in query_lower:
                tokens = sum(r.get("total_tokens", 0) for r in user_records)
                return {"tokens_used": tokens}

            # Full summary query
            if "count(*)" in query_lower and "sum(total_tokens)" in query_lower:
                return {
                    "total_requests": len(user_records),
                    "total_tokens": sum(r.get("total_tokens", 0) for r in user_records),
                    "request_tokens": sum(r.get("request_tokens", 0) for r in user_records),
                    "response_tokens": sum(r.get("response_tokens", 0) for r in user_records),
                    "estimated_cost": sum(r.get("cost_estimate", 0) for r in user_records),
                }

        # Simple SELECT 1 for health check
        if query_lower.strip() == "select 1":
            return {"1": 1}

        return None

    async def fetch_all(self, query: str, *args: Any) -> list[dict[str, Any]]:
        """Fetch multiple records."""
        query_lower = query.lower()

        # Users query
        if "from users" in query_lower:
            results = self.users.copy()
            # Apply filters based on args
            for _i, arg in enumerate(args):
                if isinstance(arg, str) and "@" in arg:  # email filter
                    results = [u for u in results if u["email"] == arg]
                elif isinstance(arg, str) and "%" in arg:  # name contains
                    search = arg.replace("%", "").lower()
                    results = [u for u in results if search in u["name"].lower()]
                elif isinstance(arg, bool):  # is_active filter
                    results = [u for u in results if u["is_active"] == arg]
            return results[:10]

        # Orders query
        if "from orders" in query_lower:
            results = self.orders.copy()
            for arg in args:
                if isinstance(arg, str):
                    if arg.startswith("user-"):
                        results = [o for o in results if o["user_id"] == arg]
                    elif arg in ("pending", "completed", "cancelled"):
                        results = [o for o in results if o["status"] == arg]
            return results[:10]

        # John Pye: List invoices
        if "from johnpye_invoices" in query_lower:
            user_id = args[0] if args else None
            results = [i for i in self.johnpye_invoices if i["user_id"] == user_id]

            # Apply filters from remaining args
            for arg in args[1:]:
                if isinstance(arg, str):
                    if arg in ("new", "paid"):
                        results = [i for i in results if i.get("invoice_status") == arg]
                    elif arg in ("paid", "preparing", "packed", "shipped", "delivered"):
                        results = [i for i in results if i.get("delivery_status") == arg]
                elif isinstance(arg, int):
                    # Limit
                    results = results[:arg]

            # Sort by created_at descending
            results.sort(key=lambda x: x.get("created_at", ""), reverse=True)
            return results

        # John Pye: List lot items for invoice
        if "from johnpye_lot_items" in query_lower:
            invoice_id = args[0] if args else None
            results = [li for li in self.johnpye_lot_items if li["invoice_id"] == invoice_id]
            results.sort(key=lambda x: x.get("lot_number", 0))
            return results

        # Conversation messages: List recent conversations for user
        if "from conversation_messages" in query_lower:
            user_id = args[0] if args else None
            limit = args[1] if len(args) > 1 else 20
            results = [
                {
                    "conversation_id": conv_id,
                    "created_at": data.get("created_at"),
                    "updated_at": data.get("updated_at"),
                    "message_count": len(data.get("messages", [])),
                }
                for conv_id, data in self.conversation_messages.items()
                if data.get("user_id") == user_id
            ]
            results.sort(key=lambda x: x.get("updated_at", ""), reverse=True)
            return results[:limit]

        # Usage records: GROUP BY model
        if "from usage_records" in query_lower and "group by model" in query_lower:
            user_id = args[0] if args else None
            user_records = [r for r in self.usage_records if r.get("user_id") == user_id]
            by_model: dict[str, int] = {}
            for r in user_records:
                model = r.get("model", "unknown")
                by_model[model] = by_model.get(model, 0) + r.get("total_tokens", 0)
            return [{"model": m, "tokens": t} for m, t in by_model.items()]

        # Usage records: GROUP BY agent_name
        if "from usage_records" in query_lower and "group by agent_name" in query_lower:
            user_id = args[0] if args else None
            user_records = [r for r in self.usage_records if r.get("user_id") == user_id]
            by_agent: dict[str, int] = {}
            for r in user_records:
                agent = r.get("agent_name", "unknown")
                by_agent[agent] = by_agent.get(agent, 0) + r.get("total_tokens", 0)
            return [{"agent_name": a, "tokens": t} for a, t in by_agent.items()]

        return []

    async def execute(self, query: str, *args: Any) -> None:
        """Execute a write query."""
        query_lower = query.lower()

        # John Pye: Insert invoice (without RETURNING - add_invoice uses this)
        if "insert into johnpye_invoices" in query_lower and "returning" not in query_lower:
            new_id = self._next_id()
            invoice = self._parse_johnpye_invoice_insert(query_lower, args, new_id)
            self.johnpye_invoices.append(invoice)
            return

        # John Pye: Insert lot item
        if "insert into johnpye_lot_items" in query_lower:
            lot_item = {
                "id": self._next_id(),
                "invoice_id": args[0],
                "lot_number": args[1],
                "description": args[2],
                "hammer_price_pence": args[3],
                "vat_pence": args[4],
                "subtotal_pence": args[5],
                "listing_id": args[6] if len(args) > 6 else None,
            }
            # Handle upsert (ON CONFLICT DO UPDATE)
            for i, existing in enumerate(self.johnpye_lot_items):
                if existing["invoice_id"] == args[0] and existing["lot_number"] == args[1]:
                    self.johnpye_lot_items[i] = lot_item
                    return
            self.johnpye_lot_items.append(lot_item)
            return

        # John Pye: Update invoice
        if "update johnpye_invoices" in query_lower:
            # Sync tool updates by ID (WHERE id = $N)
            if "where id =" in query_lower:
                invoice_id = args[-1]  # ID is last arg
                for inv in self.johnpye_invoices:
                    if inv["id"] == invoice_id:
                        # Parse SET clause to update correct fields
                        # Sync format: invoice_status, delivery_status, items_subtotal_pence, etc.
                        idx = 0
                        if "invoice_status" in query_lower:
                            inv["invoice_status"] = args[idx]
                            idx += 1
                        if "delivery_status" in query_lower:
                            inv["delivery_status"] = args[idx]
                            idx += 1
                        if "items_subtotal_pence" in query_lower:
                            inv["items_subtotal_pence"] = args[idx]
                            idx += 1
                        if "delivery_cost_pence" in query_lower:
                            inv["delivery_cost_pence"] = args[idx]
                            idx += 1
                        if "buyers_premium_pence" in query_lower:
                            inv["buyers_premium_pence"] = args[idx]
                            idx += 1
                        if "total_vat_pence" in query_lower:
                            inv["total_vat_pence"] = args[idx]
                            idx += 1
                        if "grand_total_pence" in query_lower:
                            inv["grand_total_pence"] = args[idx]
                        return
            else:
                # update_invoice_status format: updates by invoice_number and user_id
                invoice_number = args[-2]
                user_id = args[-1]
                for inv in self.johnpye_invoices:
                    if inv["user_id"] == user_id and inv["invoice_number"] == invoice_number:
                        # Update fields based on SET clause
                        if "invoice_status" in query_lower:
                            inv["invoice_status"] = args[0]
                        if "delivery_status" in query_lower:
                            idx = 1 if "invoice_status" in query_lower else 0
                            if len(args) > idx + 2:
                                inv["delivery_status"] = args[idx]
                        if "notes" in query_lower:
                            # Notes is typically the last field before invoice_number, user_id
                            inv["notes"] = args[-3]
                        return

        # John Pye: Delete invoice (cascades to lot items)
        if "delete from johnpye_invoices" in query_lower:
            user_id, invoice_number = args[0], args[1]
            for i, inv in enumerate(self.johnpye_invoices):
                if inv["user_id"] == user_id and inv["invoice_number"] == invoice_number:
                    invoice_id = inv["id"]
                    # Remove lot items
                    self.johnpye_lot_items = [
                        li for li in self.johnpye_lot_items if li["invoice_id"] != invoice_id
                    ]
                    # Remove invoice
                    self.johnpye_invoices.pop(i)
                    return

        # Conversation messages: INSERT or UPDATE (upsert pattern)
        if "insert into conversation_messages" in query_lower:
            from datetime import datetime

            conv_id = args[0]
            messages_json = args[1]

            # Parse messages JSON if string
            import json

            messages = json.loads(messages_json) if isinstance(messages_json, str) else messages_json

            now = datetime.utcnow().isoformat()
            if conv_id in self.conversation_messages:
                # Append messages (ON CONFLICT DO UPDATE)
                existing = self.conversation_messages[conv_id].get("messages", [])
                self.conversation_messages[conv_id]["messages"] = existing + messages
                self.conversation_messages[conv_id]["updated_at"] = now
            else:
                # New conversation
                self.conversation_messages[conv_id] = {
                    "conversation_id": conv_id,
                    "messages": messages,
                    "created_at": now,
                    "updated_at": now,
                }
            return

        # Usage records: INSERT
        if "insert into usage_records" in query_lower:
            from datetime import datetime

            record = {
                "user_id": args[0],
                "agent_name": args[1],
                "model": args[2],
                "request_tokens": args[3],
                "response_tokens": args[4],
                "total_tokens": args[5],
                "requests": args[6],
                "cost_estimate": args[7],
                "created_at": datetime.utcnow().isoformat(),
            }
            self.usage_records.append(record)
            return


@dataclass
class MockCache:
    """Mock cache implementation."""

    _store: dict[str, str] = field(default_factory=dict)

    async def get(self, key: str) -> str | None:
        return self._store.get(key)

    async def set(self, key: str, value: str, ttl: int | None = None) -> None:
        self._store[key] = value

    async def delete(self, key: str) -> None:
        self._store.pop(key, None)


@dataclass
class MockVectorStore:
    """Mock vector store with sample documents."""

    documents: list[dict[str, Any]] = field(default_factory=lambda: list(SAMPLE_DOCUMENTS))

    async def search(
        self, query: str, top_k: int = 10, filters: dict[str, Any] | None = None
    ) -> list[dict[str, Any]]:
        """Semantic search mock - returns documents with simulated relevance scores."""
        results = self.documents.copy()

        # Apply category filter
        if filters and "category" in filters:
            results = [d for d in results if d.get("category") == filters["category"]]

        # Simple keyword matching for demo (real impl would use embeddings)
        query_words = set(query.lower().split())
        scored = []
        for doc in results:
            title_words = set(doc["title"].lower().split())
            content_words = set(doc["content"].lower().split())
            all_words = title_words | content_words
            overlap = len(query_words & all_words)
            score = min(0.95, 0.5 + (overlap * 0.1))  # Simulated score
            scored.append({**doc, "score": score})

        # Sort by score and return top_k
        scored.sort(key=lambda x: x["score"], reverse=True)
        return scored[:top_k]

    async def get(self, doc_id: str) -> dict[str, Any] | None:
        """Get document by ID."""
        for doc in self.documents:
            if doc["id"] == doc_id:
                return doc
        return None

    async def upsert(self, doc_id: str, content: str, metadata: dict[str, Any]) -> None:
        """Insert or update a document."""
        for i, doc in enumerate(self.documents):
            if doc["id"] == doc_id:
                self.documents[i] = {"id": doc_id, "content": content, **metadata}
                return
        self.documents.append({"id": doc_id, "content": content, **metadata})


def create_mock_base_deps(user_id: str | None = "test-user") -> "BaseDeps":
    """Create BaseDeps with mock implementations."""
    from .base import BaseDeps

    return BaseDeps(
        http_client=MockHttpClient(),
        db=MockDatabase(),
        cache=MockCache(),
        user_id=user_id,
    )


def create_mock_search_deps(user_id: str | None = "test-user") -> "SearchDeps":
    """Create SearchDeps with mock implementations."""
    from .search import SearchDeps

    return SearchDeps(
        http_client=MockHttpClient(),
        db=MockDatabase(),
        cache=MockCache(),
        user_id=user_id,
        vector_store=MockVectorStore(),
        search_api_key="mock-api-key",
    )


def create_mock_auth_deps(
    user_id: str | None = "test-user",
    roles: list[str] | None = None,
    permissions: list[str] | None = None,
) -> "AuthDeps":
    """Create AuthDeps with mock implementations."""
    from .auth import AuthDeps

    return AuthDeps(
        http_client=MockHttpClient(),
        db=MockDatabase(),
        cache=MockCache(),
        user_id=user_id,
        user_roles=roles or ["user"],
        permissions=permissions or ["read:data"],
    )
