"""Strayl MCP Server - Log search tools."""

import os
from typing import Annotated, Optional
import httpx
from mcp.server.fastmcp import FastMCP

from .utils import parse_time_period, format_log_result, format_documentation_result

# Initialize FastMCP server
mcp = FastMCP(
    "Strayl Search",
    dependencies=[
        "httpx>=0.27.0",
        "python-dateutil>=2.8.0",
    ]
)


# Strayl API base URL (hardcoded)
STRAYL_API_URL = "https://ougtygyvcgdnytkswier.supabase.co/functions/v1"


def get_api_key() -> str:
    """Get API key from environment variable."""
    api_key = os.getenv("STRAYL_API_KEY", "")

    if not api_key:
        raise ValueError(
            "STRAYL_API_KEY environment variable is required. "
            "Get your API key from https://strayl.dev"
        )

    return api_key


@mcp.tool()
async def search_logs_semantic(
    query: Annotated[str, "Search query in natural language or keywords"],
    time_period: Annotated[Optional[str], "Time filter: 5m, 1h, today, yesterday, 7d, 30d, etc."] = None,
    match_threshold: Annotated[float, "Minimum similarity score (0.0 to 1.0)"] = 0.2,
    match_count: Annotated[int, "Maximum number of results to return"] = 50,
) -> str:
    """Search logs using semantic (vector) search with optional time filtering.

    This tool performs AI-powered semantic search across your logs, finding relevant entries
    even if they don't contain exact keywords."""
    try:
        api_key = get_api_key()

        # Parse time period if provided
        start_time = None
        end_time = None
        if time_period:
            start_time, end_time = parse_time_period(time_period)
            if start_time is None:
                return f"Error: Invalid time period '{time_period}'. Supported values: 5m, 1h, today, yesterday, 7d, etc."

        # Prepare request payload
        payload = {
            "query": query,
            "match_threshold": match_threshold,
            "match_count": match_count,
        }

        # Add time filters if provided
        if start_time:
            payload["start_time"] = start_time.isoformat()
        if end_time:
            payload["end_time"] = end_time.isoformat()

        # Make API request
        async with httpx.AsyncClient(timeout=30.0) as client:
            response = await client.post(
                f"{STRAYL_API_URL}/search-logs",
                json=payload,
                headers={
                    "Authorization": f"Bearer {api_key}",
                    "Content-Type": "application/json",
                },
            )

            if response.status_code != 200:
                error_data = response.json() if response.headers.get("content-type") == "application/json" else {}
                return f"Error: API returned status {response.status_code}: {error_data.get('error', response.text)}"

            data = response.json()

            if not data.get("success"):
                return f"Error: {data.get('error', 'Unknown error')}"

            results = data.get("results", [])
            total = data.get("total_results", 0)
            metadata = data.get("search_metadata", {})

            if not results:
                time_info = f" in period '{time_period}'" if time_period else ""
                return f"No logs found for query '{query}'{time_info}"

            # Format results
            output = [
                f"Semantic Search Results for: '{query}'",
                f"Total results: {total}",
            ]

            if time_period:
                output.append(f"Time period: {time_period}")

            output.append(f"Similarity threshold: {match_threshold}")
            output.append(f"Logs with embeddings: {metadata.get('logs_with_embeddings', 0)}")
            output.append("\n" + "=" * 80 + "\n")

            for i, log in enumerate(results[:10], 1):
                output.append(f"{i}. {format_log_result(log)}")
                output.append("-" * 80)

            if total > 10:
                output.append(f"\n... and {total - 10} more results")

            return "\n".join(output)

    except ValueError as e:
        return f"Configuration error: {str(e)}"
    except httpx.TimeoutException:
        return "Error: Request timed out. Please try again."
    except Exception as e:
        return f"Error: {str(e)}"


@mcp.tool()
async def search_logs_exact(
    query: Annotated[str, "Exact text to search for. Use '*' or empty string to see all logs"],
    time_period: Annotated[Optional[str], "Time filter: 5m, 1h, today, yesterday, 7d, 30d, etc."] = None,
    level: Annotated[Optional[str], "Log level filter: info, warn, error, debug"] = None,
    case_sensitive: Annotated[bool, "Whether to perform case-sensitive search"] = False,
    limit: Annotated[int, "Maximum number of results to return"] = 50,
) -> str:
    """Search logs using exact text matching with optional time and level filtering.

    This tool performs exact text search across your logs. Use '*' as query to view all logs
    with optional filters by time period and log level."""
    try:
        api_key = get_api_key()

        # Parse time period if provided
        start_time = None
        end_time = None
        if time_period:
            start_time, end_time = parse_time_period(time_period)
            if start_time is None:
                return f"Error: Invalid time period '{time_period}'"

        # Prepare request payload
        payload = {
            "query": query,
            "case_sensitive": case_sensitive,
            "limit": limit,
        }

        if level:
            if level.lower() not in ["info", "warn", "error", "debug"]:
                return f"Error: Invalid log level '{level}'. Must be one of: info, warn, error, debug"
            payload["level"] = level.lower()

        if start_time:
            payload["start_time"] = start_time.isoformat()
        if end_time:
            payload["end_time"] = end_time.isoformat()

        # Make API request to exact search endpoint
        async with httpx.AsyncClient(timeout=30.0) as client:
            response = await client.post(
                f"{STRAYL_API_URL}/exact-search-logs",
                json=payload,
                headers={
                    "Authorization": f"Bearer {api_key}",
                    "Content-Type": "application/json",
                },
            )

            if response.status_code != 200:
                error_data = response.json() if response.headers.get("content-type") == "application/json" else {}
                return f"Error: API returned status {response.status_code}: {error_data.get('error', response.text)}"

            data = response.json()

            if not data.get("success"):
                return f"Error: {data.get('error', 'Unknown error')}"

            results = data.get("results", [])
            total = data.get("total_results", 0)

            if not results:
                filters = []
                if time_period:
                    filters.append(f"period '{time_period}'")
                if level:
                    filters.append(f"level '{level}'")
                filter_str = f" with filters: {', '.join(filters)}" if filters else ""
                return f"No logs found for exact text '{query}'{filter_str}"

            # Format results
            output = [
                f"Exact Search Results for: '{query}'",
                f"Total results: {total}",
            ]

            if time_period:
                output.append(f"Time period: {time_period}")
            if level:
                output.append(f"Log level: {level}")

            output.append(f"Case sensitive: {case_sensitive}")
            output.append("\n" + "=" * 80 + "\n")

            for i, log in enumerate(results[:10], 1):
                output.append(f"{i}. {format_log_result(log)}")
                output.append("-" * 80)

            if total > 10:
                output.append(f"\n... and {total - 10} more results")

            return "\n".join(output)

    except ValueError as e:
        return f"Configuration error: {str(e)}"
    except httpx.TimeoutException:
        return "Error: Request timed out. Please try again."
    except Exception as e:
        return f"Error: {str(e)}"


@mcp.tool()
async def search_context(
    query: Annotated[str, "Search query in natural language to find relevant information across indexed context sources"],
    memory_id: Annotated[Optional[str], "Optional memory session ID to maintain conversation history and context"] = None,
    source_id: Annotated[Optional[str], "Optional UUID to limit search to a specific context source"] = None,
    use_ai: Annotated[bool, "Enable AI-powered answer structuring using Gemini 2.5 Flash (recommended)"] = True,
) -> str:
    """Search across indexed context sources using semantic vector search with AI-powered answer generation.
    
    This tool performs intelligent semantic search across your indexed content (documentation, knowledge bases, etc.)
    and structures comprehensive answers using AI. The search uses OpenAI text-embedding-3-large (2000D) for 
    high-quality semantic matching and Gemini 2.5 Flash for answer structuring.
    
    Key Features:
    - Semantic search with automatic Cyrillic→English translation
    - AI-structured answers with code examples and step-by-step explanations  
    - Conversation history support via memory_id (maintains context across queries)
    - Source-specific search with source_id filter
    - Adaptive similarity threshold (auto-adjusts if too few results)
    
    Use manage_context_memory() to create and manage memory sessions for maintaining conversation context."""
    try:
        api_key = get_api_key()

        payload = {
            "query": query,
            "limit": 15,  # Фиксированный лимит (не передается пользователем)
            "similarity_threshold": 0.22,
            "use_ai": use_ai,
        }

        if memory_id:
            payload["chat_id"] = memory_id
            
        if source_id:
            payload["source_id"] = source_id

        async with httpx.AsyncClient(timeout=60.0) as client:
            response = await client.post(
                f"{STRAYL_API_URL}/search-documentation",
                json=payload,
                headers={
                    "Authorization": f"Bearer {api_key}",
                    "Content-Type": "application/json",
                },
            )

            if response.status_code != 200:
                error_data = response.json() if response.headers.get("content-type") == "application/json" else {}
                return f"Error: API returned status {response.status_code}: {error_data.get('error', response.text)}"

            data = response.json()

            if "error" in data:
                return f"Error: {data.get('error', 'Unknown error')}"

            results = data.get("results", [])
            structured_answer = data.get("structured_answer")
            metadata = data.get("metadata", {})

            if not results and not structured_answer:
                source_info = f" in source '{source_id}'" if source_id else ""
                return f"No documentation found for query '{query}'{source_info}"

            # Минималистичный вывод - только AI ответ или краткие результаты
            if structured_answer and str(structured_answer).strip():
                # Просто AI ответ, без заголовков и метаданных
                return str(structured_answer).strip()
            
            # Fallback: краткие результаты если нет AI ответа
            output = []
            output.append(f"📚 {len(results)} результат(ов) по запросу: {query}\n")
            
            for i, result in enumerate(results, 1):
                source = result.get("source", {})
                content = result.get("content", "")[:300]  # Первые 300 символов
                source_name = source.get("name", "Unknown")
                
                output.append(f"{i}. **{source_name}**")
                output.append(f"   {content}...\n")

            return "\n".join(output)

    except ValueError as e:
        return f"Configuration error: {str(e)}"
    except httpx.TimeoutException:
        return "Error: Request timed out (Gemini processing can take up to 60s). Please try again."
    except Exception as e:
        import traceback
        return f"Error: {str(e)}\n\nTraceback:\n{traceback.format_exc()}"


@mcp.tool()
async def list_context_sources(
    include_public: Annotated[bool, "Include community-shared public context sources available to all users"] = True,
    include_private: Annotated[bool, "Include your private context sources (only visible to you)"] = True,
) -> str:
    """List all context sources (documentation, knowledge bases, etc.) available to you.
    
    Returns a comprehensive list of indexed context sources with detailed metadata including:
    - Source ID (UUID) - use this for source-specific searches
    - Name and original URL
    - Indexing status (ready, indexing, failed)
    - Visibility (public/private)
    - Statistics: number of indexed chunks, last indexed timestamp
    
    Access control:
    - Private sources: Only you can see and search (created by you)
    - Public sources: Shared across all users (indexed once, accessible to all)
    
    Use source_id parameter in search_context() to limit searches to specific sources."""
    try:
        api_key = get_api_key()
        
        payload = {
            "include_public": include_public,
            "include_private": include_private,
        }
        
        async with httpx.AsyncClient(timeout=30.0) as client:
            response = await client.post(
                f"{STRAYL_API_URL}/list-documentation-sources",
                json=payload,
                headers={
                    "Authorization": f"Bearer {api_key}",
                    "Content-Type": "application/json",
                },
            )
            
            if response.status_code != 200:
                error_data = response.json() if response.headers.get("content-type") == "application/json" else {}
                return f"Error: API returned status {response.status_code}: {error_data.get('error', response.text)}"
            
            data = response.json()
            
            if "error" in data:
                return f"Error: {data.get('error', 'Unknown error')}"
            
            sources = data.get("sources", [])
            count = data.get("count", 0)
            
            if not sources:
                filter_info = []
                if not include_public:
                    filter_info.append("excluding public")
                if not include_private:
                    filter_info.append("excluding private")
                filter_str = f" ({', '.join(filter_info)})" if filter_info else ""
                return f"No documentation sources found{filter_str}."
            
            output = [
                "=" * 80,
                "AVAILABLE DOCUMENTATION SOURCES",
                "=" * 80,
                f"Total sources: {count}",
                f"Filters: Public={'Yes' if include_public else 'No'}, Private={'Yes' if include_private else 'No'}",
                "",
            ]
            
            for i, source in enumerate(sources, 1):
                source_id = source.get("id", "Unknown")
                name = source.get("name", "Unnamed")
                url = source.get("url", "N/A")
                status = source.get("status", "unknown")
                chunks_count = source.get("chunks_count", 0)
                indexed_at = source.get("indexed_at", "")
                is_public = source.get("is_public", False)
                
                date_str = ""
                if indexed_at:
                    try:
                        from datetime import datetime
                        dt = datetime.fromisoformat(indexed_at.replace('Z', '+00:00'))
                        date_str = dt.strftime("%Y-%m-%d %H:%M")
                    except:
                        date_str = indexed_at[:10]
                
                output.append(f"{i}. {name}")
                output.append(f"   ID: {source_id}")
                output.append(f"   URL: {url}")
                output.append(f"   Status: {status.upper()}")
                output.append(f"   Public: {'Yes' if is_public else 'No (Your private source)'}")
                if chunks_count:
                    output.append(f"   Chunks: {chunks_count}")
                if date_str:
                    output.append(f"   Indexed: {date_str}")
                output.append("")
            
            output.append("=" * 80)
            output.append("\nTip: Use source_id to search within a specific context source")
            output.append("   Example: search_context('query', source_id='uuid-here')")
            
            return "\n".join(output)
            
    except ValueError as e:
        return f"Configuration error: {str(e)}"
    except httpx.TimeoutException:
        return "Error: Request timed out. Please try again."
    except Exception as e:
        import traceback
        return f"Error: {str(e)}\n\nTraceback:\n{traceback.format_exc()}"


@mcp.tool()
async def index_context(
    url: Annotated[str, "Full URL of the content to index (e.g., https://docs.example.com or https://example.com/knowledge-base)"],
    is_public: Annotated[bool, "Make this source public (accessible to all users) or private (only you)"] = True,
    force: Annotated[bool, "Force complete re-indexing even if this URL was already indexed"] = False,
) -> str:
    """Index a new context source (documentation, knowledge base, guides, etc.) from any URL.
    
    This tool performs comprehensive content indexing with the following pipeline:
    
    1. **URL Validation & Deduplication**: Checks if URL is already indexed (for public sources)
    2. **Intelligent Crawling**: 
       - Attempts sitemap.xml first for faster discovery
       - Falls back to recursive HTML crawling (max 50,000 pages, depth 10)
       - Respects sub-path scoping (e.g., /docs only crawls /docs/*)
       - Uses Jina AI Reader API for clean content extraction
    3. **Content Processing**:
       - Smart chunking (1800 chars with 200 char overlap)
       - Code-block protection (preserves code examples intact)
       - Markdown structure preservation (H1-H3 headings)
    4. **Embedding Generation**: OpenAI text-embedding-3-large (2000D) for high-quality semantic search
    5. **Database Storage**: Indexed chunks stored in Supabase with HNSW vector index
    
    Public vs Private sources:
    - Public: Indexed once, instantly available to all users (recommended for community docs)
    - Private: Only you can access (for proprietary documentation)
    
    Performance: Indexing duration depends on content size (typically 1-5 minutes for standard docs).
    
    After indexing, use search_context() to query the indexed content."""
    try:
        api_key = get_api_key()
        
        payload = {
            "url": url,
            "is_public": is_public,
            "force": force,
        }
        
        async with httpx.AsyncClient(timeout=300.0) as client:
            response = await client.post(
                f"{STRAYL_API_URL}/index-documentation",
                json=payload,
                headers={
                    "Authorization": f"Bearer {api_key}",
                    "Content-Type": "application/json",
                },
            )
            
            if response.status_code != 200:
                error_data = response.json() if response.headers.get("content-type") == "application/json" else {}
                error_msg = error_data.get('error', response.text)
                return f"Error: API returned status {response.status_code}: {error_msg}"
            
            data = response.json()
            
            if "error" in data:
                return f"Error: {data.get('error', 'Unknown error')}"
            
            if data.get("success"):
                source_id_returned = data.get("source_id", "")
                pages_crawled = data.get("pages_crawled", 0)
                chunks_indexed = data.get("chunks_indexed", 0)
                total_tokens = data.get("total_tokens", 0)
                performance = data.get("performance", {})
                
                output = [
                    "=" * 80,
                    "CONTEXT SOURCE ADDED & INDEXED",
                    "=" * 80,
                    f"URL: {url}",
                    f"Source ID: {source_id_returned}",
                    f"Public: {'Yes' if is_public else 'No (Private)'}",
                    f"Pages crawled: {pages_crawled}",
                    f"Chunks indexed: {chunks_indexed}",
                    f"Total tokens: {total_tokens:,}",
                ]
                
                if performance:
                    total_duration = performance.get("total_duration_ms", 0)
                    stages = performance.get("stages", {})
                    
                    output.append(f"\nTotal duration: {total_duration / 1000:.2f}s")
                    
                    if stages:
                        output.append("\nStage timings:")
                        for stage, duration in stages.items():
                            output.append(f"  - {stage}: {duration / 1000:.2f}s")
                
                output.append("\n" + "=" * 80)
                output.append("The documentation is now searchable!")
                output.append(f"   Use: search_documentation('your query here')")
                output.append(f"   Or: search_documentation('your query', source_id='{source_id_returned}')")
                
                return "\n".join(output)
            else:
                return f"Error: Indexing failed. {data.get('error', 'Unknown error')}"
            
    except ValueError as e:
        return f"Configuration error: {str(e)}"
    except httpx.TimeoutException:
        return "Error: Request timed out. Indexing can take several minutes. Please check the status later."
    except Exception as e:
        import traceback
        return f"Error: {str(e)}\n\nTraceback:\n{traceback.format_exc()}"


@mcp.tool()
async def manage_context_memory(
    action: Annotated[str, "Action to perform: 'list' (show all), 'create' (new memory), 'get' (view history), 'delete' (remove memory)"],
    title: Annotated[Optional[str], "Memory session title - descriptive name for the conversation thread (required for 'create')"] = None,
    memory_id: Annotated[Optional[str], "Unique memory session UUID (required for 'get' and 'delete' actions)"] = None,
    source_id: Annotated[Optional[str], "Optional context source UUID to associate memory with specific source (for 'create')"] = None,
) -> str:
    """Manage context memory sessions for maintaining conversation history and context across multiple searches.
    
    Memory sessions allow you to build context-aware conversations where each query benefits from 
    previous questions and answers. All interactions are saved with search results for future reference.
    
    Available Actions:
    - 'list': Display all your memory sessions with metadata (title, last updated, associated source)
    - 'create': Initialize a new memory session (requires title, optional source_id for source-specific memories)
    - 'get': Retrieve complete conversation history for a memory (requires memory_id)
    - 'delete': Permanently remove a memory and all its messages (requires memory_id, cascade delete)
    
    Typical Workflow:
    1. Create memory: manage_context_memory(action='create', title='My Research Topic')
    2. Search with memory: search_context('query', memory_id='uuid-from-step-1')
    3. View history: manage_context_memory(action='get', memory_id='uuid')
    
    Security: All memories are user-scoped via Row Level Security (RLS) - you can only access your own memories."""
    try:
        api_key = get_api_key()
        
        # Валидация параметров
        if action == 'create' and not title:
            return "Error: 'title' is required for creating a memory"
        
        if action in ['get', 'delete'] and not memory_id:
            return f"Error: 'memory_id' is required for action '{action}'"
        
        # Формируем URL с параметрами
        url = f"{STRAYL_API_URL}/manage-documentation-chats?action={action}"
        
        if action == 'get' or action == 'delete':
            url += f"&chat_id={memory_id}"
        
        # Подготавливаем body для POST запроса (для create)
        body = None
        if action == 'create':
            body = {"title": title}
            if source_id:
                body["source_id"] = source_id
        
        async with httpx.AsyncClient(timeout=30.0) as client:
            if body:
                response = await client.post(
                    url,
                    json=body,
                    headers={
                        "Authorization": f"Bearer {api_key}",
                        "Content-Type": "application/json",
                    },
                )
            else:
                # Для list, get, delete используем GET (можно POST с пустым body)
                response = await client.post(
                    url,
                    headers={
                        "Authorization": f"Bearer {api_key}",
                        "Content-Type": "application/json",
                    },
                )
            
            if response.status_code != 200:
                error_data = response.json() if response.headers.get("content-type") == "application/json" else {}
                return f"Error: API returned status {response.status_code}: {error_data.get('error', response.text)}"
            
            data = response.json()
            
            if "error" in data:
                return f"Error: {data.get('error', 'Unknown error')}"
            
            # Форматируем ответ в зависимости от действия
            if action == 'list':
                chats = data.get("chats", [])
                count = data.get("count", 0)
                
                if not chats:
                    return "No memories found. Create a new memory with action='create'."
                
                output = [
                    "=" * 80,
                    "YOUR CONTEXT MEMORIES",
                    "=" * 80,
                    f"Total memories: {count}",
                    "",
                ]
                
                for i, chat in enumerate(chats, 1):
                    chat_id_val = chat.get("id", "Unknown")
                    title_val = chat.get("title", "Untitled")
                    updated = chat.get("updated_at", "")
                    source = chat.get("documentation_sources")
                    
                    date_str = ""
                    if updated:
                        try:
                            from datetime import datetime
                            dt = datetime.fromisoformat(updated.replace('Z', '+00:00'))
                            date_str = dt.strftime("%Y-%m-%d %H:%M")
                        except:
                            date_str = updated[:16]
                    
                    output.append(f"{i}. {title_val}")
                    output.append(f"   ID: {chat_id_val}")
                    if source:
                        output.append(f"   Source: {source.get('name', 'N/A')}")
                    if date_str:
                        output.append(f"   Updated: {date_str}")
                    output.append("")
                
                output.append("=" * 80)
                output.append("\nTip: Use memory_id with search_context to continue conversation")
                output.append("   Example: search_context('query', memory_id='uuid-here')")
                
                return "\n".join(output)
            
            elif action == 'create':
                chat = data.get("chat", {})
                memory_id_val = chat.get("id", "Unknown")
                title_val = chat.get("title", "Untitled")
                
                return f"""✅ Memory created successfully!

Title: {title_val}
Memory ID: {memory_id_val}

Use this memory_id with search_context to save conversation history:
  search_context('your query', memory_id='{memory_id_val}')
"""
            
            elif action == 'get':
                chat = data.get("chat", {})
                messages = data.get("messages", [])
                count = data.get("count", 0)
                
                title_val = chat.get("title", "Untitled")
                
                output = [
                    "=" * 80,
                    f"MEMORY: {title_val}",
                    "=" * 80,
                    f"Messages: {count}",
                    "",
                ]
                
                if not messages:
                    output.append("No context in this memory yet.")
                else:
                    for i, msg in enumerate(messages, 1):
                        role = msg.get("role", "unknown")
                        content = msg.get("content", "")
                        created = msg.get("created_at", "")
                        
                        date_str = ""
                        if created:
                            try:
                                from datetime import datetime
                                dt = datetime.fromisoformat(created.replace('Z', '+00:00'))
                                date_str = dt.strftime("%Y-%m-%d %H:%M:%S")
                            except:
                                date_str = created[:19]
                        
                        role_emoji = "👤" if role == "user" else "🤖"
                        output.append(f"{role_emoji} {role.upper()} [{date_str}]")
                        output.append(f"{content}")
                        output.append("-" * 80)
                
                return "\n".join(output)
            
            elif action == 'delete':
                return f"✅ Memory deleted successfully (ID: {memory_id})"
            
            else:
                return f"Unknown action: {action}"
                
    except ValueError as e:
        return f"Configuration error: {str(e)}"
    except httpx.TimeoutException:
        return "Error: Request timed out. Please try again."
    except Exception as e:
        import traceback
        return f"Error: {str(e)}\n\nTraceback:\n{traceback.format_exc()}"


@mcp.tool()
def list_time_periods() -> str:
    """
    List all supported time period formats for log search.

    Returns:
        A formatted list of all supported time period values
    """
    return """Supported time periods for log search:

Minutes:
  - 5m, 5_minutes, 5_mins - Last 5 minutes
  - 10m, 10_minutes - Last 10 minutes
  - 15m, 15_minutes - Last 15 minutes
  - 30m, 30_minutes - Last 30 minutes

Hours:
  - 1h, 1_hour - Last 1 hour
  - 2h, 2_hours - Last 2 hours
  - 6h, 6_hours - Last 6 hours
  - 12h, 12_hours - Last 12 hours
  - 24h, last_24_hours - Last 24 hours

Days:
  - today - Today from 00:00 UTC
  - yesterday - Full yesterday (00:00 to 23:59)
  - 7d, last_7_days - Last 7 days
  - 30d, last_30_days - Last 30 days

Examples:
  - search_logs_semantic("error connecting to database", "1h")
  - search_logs_exact("timeout", "today", level="error")
"""