"""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_documentation(
    query: Annotated[str, "Search query in natural language to find relevant documentation"],
    source_id: Annotated[Optional[str], "Optional source ID to search within specific documentation source"] = None,
    limit: Annotated[int, "Maximum number of results to return"] = 5,
    similarity_threshold: Annotated[float, "Minimum similarity score (0.0 to 1.0)"] = 0.7,
    use_ai: Annotated[bool, "Use AI (Gemini) to structure the answer"] = True,
) -> str:
    """Search documentation using semantic (vector) search with AI-powered answer structuring."""
    try:
        api_key = get_api_key()

        payload = {
            "query": query,
            "limit": limit,
            "similarity_threshold": similarity_threshold,
            "use_ai": use_ai,
        }

        if source_id:
            payload["source_id"] = source_id

        # ✅ Увеличен timeout до 60 секунд для Gemini
        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()
            
            # 🐛 DEBUG DIAGNOSTICS (Added to debug JSON structure mismatch)
            debug_info = {
                "response_keys": list(data.keys()),
                "structured_answer_raw": data.get("structured_answer"),
                "structured_answer_type": str(type(data.get("structured_answer"))),
                "metadata_raw": data.get("metadata"),
                "ai_processed_flag": data.get("metadata", {}).get("ai_processed")
            }

            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:
                source_info = f" in source '{source_id}'" if source_id else ""
                return f"No documentation found for query '{query}'{source_info}"

            # Build output
            output = []
            
            # Header
            output.append("=" * 80)
            output.append(f"📚 DOCUMENTATION SEARCH: '{query}'")
            output.append("=" * 80)
            output.append(f"Results: {len(results)} | Duration: {metadata.get('duration_ms', 0)}ms | AI: {metadata.get('ai_processed', False)}")
            output.append("")

            # ✅ AI-STRUCTURED ANSWER (если есть)
            if structured_answer and str(structured_answer).strip():
                output.append("🤖 AI-STRUCTURED ANSWER (Gemini 2.5 Flash)")
                output.append("=" * 80)
                output.append(str(structured_answer).strip())
                output.append("")
                
                # 🐛 DEBUG OUTPUT (Only if AI answer exists)
                output.append("🐛 DEBUG DIAGNOSTICS")
                output.append("-" * 80)
                output.append(f"Keys received: {debug_info['response_keys']}")
                output.append(f"Structured Answer Type: {debug_info['structured_answer_type']}")
                output.append(f"Metadata Raw: {debug_info['metadata_raw']}")
                output.append("=" * 80)
                
                output.append("📄 SOURCE DOCUMENTS (for reference)")
                output.append("=" * 80)
            else:
                # 🐛 DEBUG OUTPUT (If no AI answer)
                output.append("🐛 DEBUG DIAGNOSTICS (Why no AI answer?)")
                output.append("-" * 80)
                output.append(f"Keys received: {debug_info['response_keys']}")
                output.append(f"Structured Answer Type: {debug_info['structured_answer_type']}")
                output.append(f"Structured Answer Value: {str(debug_info['structured_answer_raw'])[:200]}")
                output.append(f"Metadata Raw: {debug_info['metadata_raw']}")
                output.append("=" * 80)
                
                output.append("📄 SEARCH RESULTS")
                output.append("=" * 80)

            # Source documents
            for i, result in enumerate(results, 1):
                output.append(f"\n{i}. {format_documentation_result(result)}")

            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_documentation_sources(
    include_public: Annotated[bool, "Include public sources accessible to all users"] = True,
    include_private: Annotated[bool, "Include your private sources"] = True,
) -> str:
    """List documentation sources available to you.
    
    Returns a formatted list of documentation sources with their IDs, names, URLs, 
    and status. Shows only sources you have access to:
    - Your private sources (if include_private=True)
    - Public sources (if include_public=True)
    
    Use source_id to search within a specific documentation source."""
    try:
        api_key = get_api_key()
        
        # ✅ ИСПРАВЛЕНО: Используем Edge Function для получения списка источников
        # Edge Function будет фильтровать источники по user_id из API ключа
        # Это гарантирует, что пользователь видит только свои источники + публичные
        
        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("\n💡 Tip: Use source_id to search within a specific documentation source")
            output.append("   Example: search_documentation('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_documentation(
    url: Annotated[str, "URL of the documentation to index (e.g., https://docs.example.com)"],
    is_public: Annotated[bool, "Whether this documentation should be public (visible to all users who add it)"] = True,
    force: Annotated[bool, "Force re-indexing even if already indexed"] = False,
    max_pages: Annotated[int, "Maximum number of pages to crawl"] = 100,
    max_depth: Annotated[int, "Maximum crawling depth"] = 3,
) -> str:
    """Add and index documentation from a URL.
    
    This tool will:
    1. Check if the documentation already exists (for public docs)
    2. Add it to your documentation list
    3. Crawl the website and extract content
    4. Generate embeddings for semantic search
    5. Make it searchable via search_documentation
    
    If the documentation is public and already indexed by another user, it will be added
    to your list without re-indexing (unless you use force=True).
    
    Note: Indexing can take several minutes depending on the size of the documentation."""
    try:
        api_key = get_api_key()
        
        # Подготавливаем payload для Edge Function
        payload = {
            "url": url,
            "is_public": is_public,
            "force": force,
            "max_pages": max_pages,
            "max_depth": max_depth,
        }
        
        # Вызываем Edge Function index-documentation
        # Увеличиваем timeout до 300 секунд (5 минут) для индексации
        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,
                    "✅ DOCUMENTATION 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()
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")
"""