"""
Dynamic Learning Path Generator MCP Server

Uses existing MCP servers (Brave Search, Fetch, Memory) to dynamically generate
learning paths by searching for resources and compiling them into structured paths.
"""

from mcp.server.fastmcp import FastMCP
from learning_data import (
    save_path,
    get_path,
    mark_step_completed,
    get_next_step,
    get_path_progress
)
import json
import uuid
from datetime import datetime
from typing import Union, List, Dict, Any

mcp = FastMCP("learning_path_server")


@mcp.tool()
async def generate_path(goal: str, level: str = "beginner") -> str:
    """
    Generate a learning path by searching for resources dynamically.
    This tool creates a path structure that will be populated by the path generator agent.
    
    Args:
        goal: The learning goal (e.g., "Learn Python", "Web Development", "Machine Learning")
        level: The skill level - "beginner", "intermediate", or "advanced" (default: "beginner")
    
    Returns:
        JSON string with the learning path path_id. The actual path content will be generated
        by the path generator agent using search tools.
    """
    try:
        path_id = f"path-{uuid.uuid4().hex[:8]}"
        
        # Create initial path structure
        path = {
            "path_id": path_id,
            "goal": goal,
            "level": level,
            "status": "generating",  # Will be updated by the generator agent
            "created_at": datetime.now().isoformat(),
            "steps": []
        }
        
        save_path(path)
        
        return json.dumps({
            "path_id": path_id,
            "goal": goal,
            "level": level,
            "status": "Path structure created. Use the path generator agent to populate it with resources.",
            "message": "Path structure created. The learning path will be generated by searching for resources."
        }, indent=2)
    except Exception as e:
        return json.dumps({"error": str(e)}, indent=2)


@mcp.tool()
async def update_path_content(path_id: str, steps: Union[str, List[Dict[str, Any]], Dict[str, Any]]) -> str:
    """
    Update a learning path with generated steps and resources.
    This is called by the path generator agent after searching for resources.
    
    Args:
        path_id: The learning path ID
        steps: Can be a JSON string or a list/dict of steps, each containing title, description, resources
    
    Returns:
        JSON string with confirmation
    """
    try:
        path = get_path(path_id)
        if path is None:
            return json.dumps({"error": f"Path {path_id} not found"}, indent=2)
        
        # Handle different input types: string, list, or dict
        if isinstance(steps, str):
            # Try to parse as JSON string
            try:
                steps_data = json.loads(steps)
            except json.JSONDecodeError:
                # If JSON parsing fails, try to extract JSON from the string
                import re
                json_match = re.search(r'\[.*\]', steps, re.DOTALL)
                if json_match:
                    steps_data = json.loads(json_match.group())
                else:
                    raise ValueError(f"Invalid JSON string in steps parameter")
        elif isinstance(steps, list):
            # Already a list, use directly
            steps_data = steps
        elif isinstance(steps, dict):
            # Single step as dict, wrap in list
            steps_data = [steps]
        else:
            raise ValueError(f"Steps must be a string, list, or dict, got {type(steps)}")
        
        # Convert to path format
        path["steps"] = []
        for idx, step in enumerate(steps_data, 1):
            # Ensure step is a dict
            if isinstance(step, str):
                try:
                    step = json.loads(step)
                except json.JSONDecodeError:
                    # If it's not valid JSON, treat as a simple title
                    step = {"title": step}
            elif not isinstance(step, dict):
                # Skip invalid steps
                print(f"Warning: Skipping invalid step at index {idx}: {step}")
                continue
            
            # Extract step data with defaults
            path["steps"].append({
                "id": f"step-{idx}",
                "title": step.get("title", f"Step {idx}"),
                "description": step.get("description", ""),
                "resources": step.get("resources", []),
                "estimated_time": step.get("estimated_time", "2-3 hours"),
                "completed": False
            })
        
        path["status"] = "ready"
        path["updated_at"] = datetime.now().isoformat()
        
        save_path(path)
        
        print(f"Path {path_id} updated with {len(path['steps'])} steps")
        
        return json.dumps({
            "path_id": path_id,
            "status": "updated",
            "steps_count": len(path["steps"]),
            "message": f"Successfully updated path with {len(path['steps'])} steps"
        }, indent=2)
    except Exception as e:
        import traceback
        error_detail = traceback.format_exc()
        print(f"Error updating path content: {error_detail}")
        return json.dumps({
            "error": str(e),
            "detail": error_detail
        }, indent=2)


@mcp.tool()
async def mark_completed(path_id: str, step_id: str) -> str:
    """
    Mark a learning step as completed.
    
    Args:
        path_id: The ID of the learning path
        step_id: The ID of the step to mark as completed (e.g., "step-1", "step-2")
    
    Returns:
        JSON string with updated progress information
    """
    try:
        result = mark_step_completed(path_id, step_id)
        return json.dumps(result, indent=2)
    except Exception as e:
        return json.dumps({"error": str(e)}, indent=2)


@mcp.tool()
async def get_next_step(path_id: str) -> str:
    """
    Get the next uncompleted step in a learning path.
    
    Args:
        path_id: The ID of the learning path
    
    Returns:
        JSON string with the next step to work on, or a completion message if all steps are done
    """
    try:
        result = get_next_step(path_id)
        if result is None:
            return json.dumps({"error": f"Path {path_id} not found"}, indent=2)
        return json.dumps(result, indent=2)
    except Exception as e:
        return json.dumps({"error": str(e)}, indent=2)


@mcp.tool()
async def get_path_details(path_id: str) -> str:
    """
    Get full details of a learning path including all steps and progress.
    
    Args:
        path_id: The ID of the learning path
    
    Returns:
        JSON string with complete path information
    """
    try:
        path = get_path(path_id)
        if path is None:
            return json.dumps({"error": f"Path {path_id} not found"}, indent=2)
        
        progress = get_path_progress(path_id)
        
        result = {
            **path,
            "progress": progress
        }
        
        return json.dumps(result, indent=2)
    except Exception as e:
        return json.dumps({"error": str(e)}, indent=2)


@mcp.resource("learning://path/{path_id}")
async def read_path_resource(path_id: str) -> str:
    """
    Resource that provides learning path details.
    This can be read by agents to get context about a learning path.
    """
    try:
        path = get_path(path_id)
        if path is None:
            return f"Learning path {path_id} not found."
        
        progress = get_path_progress(path_id)
        completed_steps = progress.get("completed_steps", [])
        
        output = f"Learning Path: {path['goal']}\n"
        output += f"Level: {path.get('level', 'N/A')}\n"
        output += f"Status: {path.get('status', 'unknown')}\n"
        output += f"Progress: {len(completed_steps)}/{len(path.get('steps', []))} steps completed\n\n"
        
        if path.get('steps'):
            output += "Steps:\n"
            for idx, step in enumerate(path.get("steps", []), 1):
                status = "✓" if step["id"] in completed_steps else "○"
                output += f"{status} Step {idx}: {step['title']}\n"
                if step.get("description"):
                    output += f"   {step['description']}\n"
                if step.get("estimated_time"):
                    output += f"   Estimated time: {step['estimated_time']}\n"
                if step.get("resources"):
                    output += f"   Resources: {len(step['resources'])} available\n"
                output += "\n"
        else:
            output += "Path is being generated...\n"
        
        return output
    except Exception as e:
        return f"Error reading path: {str(e)}"


if __name__ == "__main__":
    mcp.run(transport="stdio")
