{
    "name": "MangaDex Reader",
    "version": "1.0.0",
    "author": "Animex",
    "description": "Fetches manga chapters and page images from MangaDex using their v5 API.",
    "type": "MANGA_READER",
    "requirements": ["httpx"]
}
---
import asyncio
import httpx
from typing import Optional, List, Dict, Any

# --- Helper Functions ---

def _uses_hybrid_client() -> bool:
    return not hasattr(httpx, "AsyncClient")

async def _fetch_json(url: str, params: Dict[str, Any] = None, headers: Dict[str, str] = None, timeout: int = 10) -> Dict[str, Any]:
    if _uses_hybrid_client():
        resp = await httpx.get(url, params=params, headers=headers, timeout=timeout)
    else:
        async with httpx.AsyncClient() as client:
            resp = await client.get(url, params=params, headers=headers, timeout=timeout)
    resp.raise_for_status()
    return resp.json()

async def get_title_from_mal(mal_id: int, client: httpx.AsyncClient) -> Optional[str]:
    """
    Fetches the primary English or Romaji title from Jikan (MAL API) 
    to use for searching MangaDex.
    """
    url = f"https://api.jikan.moe/v4/manga/{mal_id}"
    try:
        data = await _fetch_json(url)
        # Prefer English title for search accuracy, fallback to default title
        return data.get("data", {}).get("title_english") or data.get("data", {}).get("title")
    except Exception as e:
        print(f"MangaDex-Module: Jikan API error: {e}")
        return None

async def find_mangadex_id(mal_id: int, title: str, client: httpx.AsyncClient) -> Optional[str]:
    """
    Searches MangaDex for the title and verifies the MAL ID in the metadata 
    to ensure we have the correct manga.
    """
    search_url = "https://api.mangadex.org/manga"
    params = {
        "title": title,
        "limit": 10,
        "order[relevance]": "desc"
    }
    
    try:
        data = await _fetch_json(search_url, params=params)
        results = data.get("data", [])
        
        for manga in results:
            attributes = manga.get("attributes", {})
            links = attributes.get("links", {})
            
            # Check if the MAL ID provided in MangaDex metadata matches our target
            # Note: links['mal'] is a string in their API
            if links.get("mal") == str(mal_id):
                return manga["id"]
        
        # Fallback: If no strict MAL ID match found, return the first result 
        # if the titles are very similar (basic loose match)
        if results:
             print(f"MangaDex-Module: Strict MAL ID match failed. Defaulting to top search result: {results[0]['attributes']['title']}")
             return results[0]["id"]
             
        return None

    except Exception as e:
        print(f"MangaDex-Module: Search failed: {e}")
        return None

# --- Main Module Functions ---

async def get_chapters(mal_id: int) -> Optional[List[Dict[str, Any]]]:
    """
    Asynchronously gets a list of chapters for a given MyAnimeList ID 
    via MangaDex API.
    """
    title = await get_title_from_mal(mal_id, httpx)
    if not title:
        print("MangaDex-Module: Could not retrieve title from MAL.")
        return None

    md_id = await find_mangadex_id(mal_id, title, httpx)
    if not md_id:
        print(f"MangaDex-Module: Could not find MangaDex ID for MAL ID {mal_id}")
        return None

    feed_url = f"https://api.mangadex.org/manga/{md_id}/feed"
    params = {
        "translatedLanguage[]": "en",
        "order[chapter]": "desc",
        "limit": 500,
        "includes[]": "scanlation_group"
    }
    
    try:
        data = await _fetch_json(feed_url, params=params)
        chapters = data.get("data", [])

        formatted_chapters = []
        seen_chapters = set()

        for ch in chapters:
            attr = ch.get("attributes", {})
            chapter_num = attr.get("chapter")
            
            if chapter_num is None:
                continue

            if chapter_num in seen_chapters:
                continue
            seen_chapters.add(chapter_num)

            chapter_title = attr.get("title") or f"Chapter {chapter_num}"
            formatted_chapters.append({
                "title": chapter_title,
                "url": ch["id"],
                "chapter_number": str(chapter_num)
            })
            
        return formatted_chapters

    except Exception as e:
        print(f"MangaDex-Module: Error fetching chapters: {e}")
        return None

async def get_chapter_images(mal_id: int, chapter_num: str) -> Optional[List[str]]:
    """
    Asynchronously gets page image URLs for a specific chapter number.
    Note: 'chapter_num' is used to look up the UUID from the chapter list logic.
    """
    # 1. We need the Chapter UUID. Re-using get_chapters to map Num -> UUID.
    # In a production app, you might cache the chapter list to avoid this extra call.
    all_chapters = await get_chapters(mal_id)
    if not all_chapters:
        return None

    chapter_uuid = None
    for ch in all_chapters:
        if ch.get("chapter_number") == str(chapter_num):
            chapter_uuid = ch.get("url") # This contains the UUID from get_chapters
            break
    
    if not chapter_uuid:
        print(f"MangaDex-Module: Chapter {chapter_num} not found for MAL ID {mal_id}")
        return None

    # 2. Call MangaDex At-Home API to get image metadata
    async with httpx.AsyncClient() as client:
        try:
            at_home_url = f"https://api.mangadex.org/at-home/server/{chapter_uuid}"
            resp = await client.get(at_home_url, timeout=10)
            resp.raise_for_status()
            
            data = resp.json()
            base_url = data.get("baseUrl")
            chapter_hash = data.get("chapter", {}).get("hash")
            # 'data' contains full quality, 'dataSaver' contains compressed
            filenames = data.get("chapter", {}).get("data", [])

            if not base_url or not chapter_hash or not filenames:
                print("MangaDex-Module: Incomplete data received from At-Home API.")
                return []

            # 3. Construct direct image URLs
            # Format: {baseUrl}/data/{hash}/{filename}
            image_links = [
                f"{base_url}/data/{chapter_hash}/{filename}" 
                for filename in filenames
            ]
            
            return image_links

        except Exception as e:
            print(f"MangaDex-Module: Error fetching images: {e}")
            return None