import logging
import os 
import sys
import json

from typing import Any
from abi_core.common.types import ServerConfig

# ABI Logging Configuration
_abi_logger = None

def _setup_abi_logger():
    """Setup ABI logger with environment variable configuration."""
    global _abi_logger
    
    if _abi_logger is not None:
        return _abi_logger
    
    # Check if debug logging is enabled via environment variable
    debug_enabled = os.getenv('ABI_SETTINGS_LOGGING_DEBUG', 'False').lower() in ('true', '1', 'yes', 'on')
    
    # Create logger
    _abi_logger = logging.getLogger('abi_logger')
    _abi_logger.setLevel(logging.DEBUG if debug_enabled else logging.INFO)
    
    # Clear any existing handlers to avoid duplicates
    _abi_logger.handlers.clear()
    
    # Create handler that writes to stdout
    handler = logging.StreamHandler(sys.stdout)
    handler.setLevel(logging.DEBUG if debug_enabled else logging.INFO)
    
    # Create formatter
    formatter = logging.Formatter(
        '%(asctime)s - ABI - %(levelname)s - %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S'
    )
    handler.setFormatter(formatter)
    
    # Add handler to logger
    _abi_logger.addHandler(handler)
    
    # Prevent propagation to avoid duplicate logs
    _abi_logger.propagate = False
    
    return _abi_logger

def abi_logging(message: str, level: str = 'info'):
    """
    ABI centralized logging function.
    
    Args:
        message (str): The message to log
        level (str): Log level ('debug', 'info', 'warning', 'error', 'critical')
    
    Usage:
        from common.utils import abi_logging
        abi_logging("My debug message")
        abi_logging("Error occurred", "error")
    """
    logger = _setup_abi_logger()
    
    level = level.lower()
    if level == 'debug':
        logger.debug(message)
    elif level == 'info':
        logger.info(message)
    elif level == 'warning':
        logger.warning(message)
    elif level == 'error':
        logger.error(message)
    elif level == 'critical':
        logger.critical(message)
    else:
        logger.info(message)  # Default to info if invalid level

import re

# Parse SEMANTIC_LAYER_HOST which may contain protocol and port
SEMANTIC_LAYER_URL = os.getenv('SEMANTIC_LAYER_HOST', 'http://abi-semantic-layer:10100')
# MCP_TRANSPORT supports 'sse' or 'streamable-http'
MCP_TRANSPORT = os.getenv('MCP_TRANSPORT', 'streamable-http')

# Extract host and port from URL if provided with protocol
# Supports formats: http://host:port/path or just host
url_match = re.match(r'https?://([^:/]+)(?::(\d+))?', SEMANTIC_LAYER_URL)
if url_match:
    HOST = url_match.group(1)
    PORT = int(url_match.group(2)) if url_match.group(2) else 10100
    URL = SEMANTIC_LAYER_URL
else:
    # Fallback: treat as plain hostname
    HOST = SEMANTIC_LAYER_URL.split(':')[0]
    PORT = int(os.getenv('SEMANTIC_LAYER_PORT', 10100))
    URL = f'http://{HOST}:{PORT}'

def get_mcp_server_config() -> ServerConfig:
    """Get the MCP server configuration.
    
    Parses SEMANTIC_LAYER_HOST environment variable which can be:
    - Full URL: http://hostname:port
    - Hostname only: hostname (uses SEMANTIC_LAYER_PORT or default 10100)
    
    Transport can be configured via MCP_TRANSPORT environment variable:
    - 'sse' (default): Server-Sent Events transport
    - 'streamable-http': Streamable HTTP transport
    
    Returns:
        ServerConfig with parsed host, port, transport, and url
    """
    abi_logging(f'[*] MCP Config: Host={HOST}, Port={PORT}, Transport={MCP_TRANSPORT}')
    return ServerConfig(
        host=HOST,
        port=PORT,
        transport=MCP_TRANSPORT,
        url=URL,
    )

def truncate(obj: Any, max_chars: int = 4000) -> str:
    """Convierte a JSON y recorta para no exceder num_ctx."""
    text = json.dumps(obj, ensure_ascii=False)
    return text if len(text) <= max_chars else text[:max_chars] + "…"


# ── LLM response helpers ───────────────────────────────────────

def clean_llm_json(raw: str) -> dict:
    """Clean and parse a JSON response from an LLM.

    Handles common LLM quirks:
    - Markdown code fences (```json ... ```)
    - Double braces {{ }} from template confusion
    - Leading/trailing whitespace

    Returns parsed dict on success, or a fallback single-task plan
    on parse failure.

    Args:
        raw: Raw LLM text output.

    Returns:
        Parsed dict.
    """
    cleaned = raw.strip()

    # Strip markdown code fences
    if cleaned.startswith('```json'):
        cleaned = cleaned[7:]
    if cleaned.startswith('```'):
        cleaned = cleaned[3:]
    if cleaned.endswith('```'):
        cleaned = cleaned[:-3]
    cleaned = cleaned.strip()

    # Fix double braces from LLM hallucination
    cleaned = cleaned.replace('{{', '{').replace('}}', '}')

    try:
        return json.loads(cleaned)
    except json.JSONDecodeError:
        abi_logging(f"[⚠️] Failed to parse LLM JSON, returning fallback", level="warning")
        return {
            "status": "ready",
            "plan": {
                "objective": "Complete user request",
                "tasks": [{
                    "task_id": "task_1",
                    "description": raw,
                    "agents": [],
                    "agent_count": 1,
                    "dependencies": [],
                }],
                "execution_strategy": "sequential",
            },
        }


def format_plan_summary(plan: dict) -> str:
    """Format a plan dict into a human-readable markdown summary.

    Args:
        plan: Dict with ``objective``, ``tasks``, ``execution_strategy``.

    Returns:
        Formatted string.
    """
    objective = plan.get("objective", "Complete user request")
    tasks = plan.get("tasks", [])
    strategy = plan.get("execution_strategy", "sequential")

    lines = [
        "📋 **Plan Created**\n",
        f"🎯 **Objective:** {objective}\n",
        f"📊 **Strategy:** {strategy.capitalize()}\n",
        f"**Tasks ({len(tasks)}):**\n",
    ]

    for i, t in enumerate(tasks, 1):
        tid = t.get("task_id", f"task_{i}")
        desc = t.get("description", "")
        agents = t.get("agents", [])
        deps = t.get("dependencies", [])

        lines.append(f"\n{i}. **{tid}:** {desc}")
        if agents and agents[0]:
            names = [a.get("name", "Unknown") for a in agents if a]
            lines.append(f"   👤 Agent(s): {', '.join(names)}")
        else:
            lines.append("   ⚠️ No agent assigned")
        if deps:
            lines.append(f"   🔗 Depends on: {', '.join(deps)}")

    lines.append("\n✅ Plan ready for execution by Orchestrator")
    return "\n".join(lines)
