import httpx
from mcp.server.fastmcp import FastMCP, Context
import os

# Create a Dev.to MCP server
mcp = FastMCP("Dev.to MCP Server")

# Constants
BASE_URL = "https://dev.to/api"

# Helper functions
async def fetch_from_api(path: str, params: dict = None) -> dict:
    """Helper function to fetch data from Dev.to API"""
    async with httpx.AsyncClient() as client:
        url = f"{BASE_URL}{path}"
        response = await client.get(url, params=params, timeout=10.0)
        response.raise_for_status()
        return response.json()

# Resources

@mcp.tool()
async def get_latest_articles() -> str:
    """Get the latest articles from Dev.to"""
    articles = await fetch_from_api("/articles/latest")
    return format_articles(articles[:10])  # Limiting to 10 for readability
    
@mcp.tool()
async def get_top_articles() -> str:
    """Get the top articles from Dev.to"""
    articles = await fetch_from_api("/articles")
    return format_articles(articles[:10])  # Limiting to 10 for readability

@mcp.tool()
async def get_articles_by_tag(tag: str) -> str:
    """Get articles by tag from Dev.to"""
    articles = await fetch_from_api("/articles", params={"tag": tag})
    return format_articles(articles[:10])  # Limiting to 10 for readability

@mcp.tool()
async def get_article_by_id(id: str) -> str:
    """Get a specific article by ID from Dev.to"""
    article = await fetch_from_api(f"/articles/{id}")
    return format_article_details(article)

# Tools

@mcp.tool()
async def search_articles(query: str, page: int = 1) -> str:
    """
    Search for articles on Dev.to
    
    Args:
        query: Search term to find articles
        page: Page number for pagination (default: 1)
    """
    articles = await fetch_from_api("/articles", params={"page": page})
    
    filtered_articles = [
        article for article in articles 
        if query.lower() in article.get("title", "").lower() or 
           query.lower() in article.get("description", "").lower()
    ]
    
    return format_articles(filtered_articles[:10])

@mcp.tool()
async def get_article_details(article_id: int) -> str:
    """
    Get detailed information about a specific article
    
    Args:
        article_id: The ID of the article to retrieve
    """
    article = await fetch_from_api(f"/articles/{article_id}")
    return format_article_details(article)

@mcp.tool()
async def get_articles_by_username(username: str) -> str:
    """
    Get articles written by a specific user
    
    Args:
        username: The username of the author
    """
    articles = await fetch_from_api("/articles", params={"username": username})
    return format_articles(articles[:10])

@mcp.tool()
async def get_user_info(username: str) -> str:
    """
    Get information about a Dev.to user
    
    Args:
        username: The username of the user
    """
    try:
        user = await fetch_from_api(f"/users/{username}")
        return format_user_profile(user)
    except httpx.HTTPStatusError as e:
        if e.response.status_code == 404:
            return f"User {username} not found."
        raise e

@mcp.tool()
async def create_article(title: str, body_markdown: str, tags: str = "", published: bool = False) -> str:
    """
    Create and publish a new article on Dev.to
    
    Args:
        title: The title of the article
        body_markdown: The content of the article in markdown format
        tags: Comma-separated list of tags (e.g., "python,tutorial,webdev")
        published: Whether to publish immediately (True) or save as draft (False)
    """
    article_data = {
        "article": {
            "title": title,
            "body_markdown": body_markdown,
            "published": published,
            "tags": tags
        }
    }
    
    async with httpx.AsyncClient() as client:
        response = await client.post(f"{BASE_URL}/articles", json=article_data, headers={"Content-Type": "application/json", "api-key": os.getenv("DEV_TO_API_KEY")}, timeout=10.0)
        response.raise_for_status()
        article = response.json()
        
    return f"Article created successfully with ID: {article.get('id')}\nURL: {article.get('url')}"

@mcp.tool()
async def update_article(article_id: int, title: str = None, body_markdown: str = None, 
                        tags: str = None, published: bool = None) -> str:
    """
    Update an existing article on Dev.to
    
    Args:
        article_id: The ID of the article to update
        title: New title for the article (optional)
        body_markdown: New content in markdown format (optional)
        tags: New comma-separated list of tags (optional)
        published: Change publish status (optional)
    """
    # First get the current article data
    article = await fetch_from_api(f"/articles/{article_id}")
    
    # Prepare update data with only the fields that are provided
    update_data = {"article": {}}
    if title is not None:
        update_data["article"]["title"] = title
    if body_markdown is not None:
        update_data["article"]["body_markdown"] = body_markdown
    if tags is not None:
        update_data["article"]["tags"] = tags
    if published is not None:
        update_data["article"]["published"] = published
    
    async with httpx.AsyncClient() as client:
        response = await client.put(f"{BASE_URL}/articles/{article_id}", json=update_data, timeout=10.0)
        response.raise_for_status()
        updated_article = response.json()
    
    return f"Article updated successfully\nURL: {updated_article.get('url')}"

# Helper formatting functions

def format_articles(articles: list) -> str:
    """Format a list of articles for display"""
    if not articles:
        return "No articles found."
    
    result = "# Dev.to Articles\n\n"
    for article in articles:
        title = article.get("title", "Untitled")
        author = article.get("user", {}).get("name", "Unknown Author")
        published_date = article.get("readable_publish_date", "Unknown date")
        article_id = article.get("id", "")
        tags = article.get("tags", "")
        
        result += f"## {title}\n"
        result += f"ID: {article_id}\n"
        result += f"Author: {author}\n"
        result += f"Published: {published_date}\n"
        result += f"Tags: {tags}\n"
        result += f"Description: {article.get('description', 'No description available.')}\n\n"
    
    return result

def format_article_details(article: dict) -> str:
    """Format a single article with full details"""
    if not article:
        return "Article not found."
    
    title = article.get("title", "Untitled")
    author = article.get("user", {}).get("name", "Unknown Author")
    published_date = article.get("readable_publish_date", "Unknown date")
    body = article.get("body_markdown", "No content available.")
    tags = article.get("tags", "")
    
    result = f"# {title}\n\n"
    result += f"Author: {author}\n"
    result += f"Published: {published_date}\n"
    result += f"Tags: {tags}\n\n"
    result += "## Content\n\n"
    result += body
    
    return result

def format_user_profile(user: dict) -> str:
    """Format a user profile for display"""
    if not user:
        return "User not found."
    
    username = user.get("username", "Unknown")
    name = user.get("name", "Unknown")
    bio = user.get("summary", "No bio available.")
    twitter = user.get("twitter_username", "")
    github = user.get("github_username", "")
    website = user.get("website_url", "")
    location = user.get("location", "")
    joined = user.get("joined_at", "")
    
    result = f"# {name} (@{username})\n\n"
    result += f"Bio: {bio}\n\n"
    
    result += "## Details\n"
    if location:
        result += f"Location: {location}\n"
    if joined:
        result += f"Member since: {joined}\n"
    
    result += "\n## Links\n"
    if twitter:
        result += f"Twitter: @{twitter}\n"
    if github:
        result += f"GitHub: {github}\n"
    if website:
        result += f"Website: {website}\n"
    
    return result



def main():
    """Main entry point for the Dev.to MCP server."""
    print("Starting Dev.to MCP server...")
    mcp.run()

if __name__ == "__main__":
    main()