import csv
import os
import config
import difflib
import httpx
import re
from typing import List, Dict, Any, Optional
from tools.utils import ToolError, format_count, get_type_suffix

# Global cache to avoid reading the CSV from disk on every request
_TAG_CACHE: Optional[List[Dict[str, Any]]] = None

def _load_tags() -> List[Dict[str, Any]]:
    """Reads the Danbooru tag CSV and caches it in memory."""
    tag_file_path = config.TAG_DATABASE_PATH
    tags = []
    
    if not os.path.exists(tag_file_path):
        # We raise ToolError here because it's a fatal configuration issue
        raise ToolError(f"Tag database not found at {tag_file_path}. Please ensure the tag-autocomplete extension is installed.")

    try:
        with open(tag_file_path, mode='r', encoding='utf-8') as f:
            reader = csv.reader(f)
            for row in reader:
                if not row or len(row) < 3:
                    continue
                
                name = row[0]
                tag_type = row[1]
                count = int(row[2]) if row[2].isdigit() else 0
                aliases = row[3].lower().split(',') if len(row) > 3 else []

                tags.append({
                    "name": name,
                    "name_lower": name.lower(),
                    "type": tag_type,
                    "count": count,
                    "aliases": aliases
                })
    except Exception as e:
        raise ToolError(f"Error reading tag database: {str(e)}")
    
    return tags

    return tags

async def _fetch_wiki_info(tag_name: str) -> Optional[str]:
    """Fetches the wiki description for a tag from Danbooru."""
    if not config.ENABLE_TAG_WIKI:
        return None
    
    url = f"https://danbooru.donmai.us/wiki_pages/{tag_name}.json"
    try:
        # Use Basic Auth if credentials are provided
        auth = None
        if config.DANBOORU_LOGIN and config.DANBOORU_API_KEY:
            auth = (config.DANBOORU_LOGIN, config.DANBOORU_API_KEY)

        async with httpx.AsyncClient(timeout=5.0) as client:
            # Using a browser-like UA to avoid potential blocks
            headers = {"User-Agent": config.USER_AGENT}
            resp = await client.get(url, headers=headers, auth=auth)
            
            if resp.status_code == 403:
                return " (Wiki access blocked by Danbooru/Cloudflare)"
            
            if resp.status_code == 200:
                data = resp.json()
                wiki_body = data.get("wiki_page", {}).get("body")
                if wiki_body:
                    # Strip HTML tags for the LLM
                    return re.sub(r'<[^>]*>', '', wiki_body).strip()
    except Exception as e:
        return f" (Error fetching wiki: {str(e)})"
    return None

async def handle(args: Dict[str, Any]) -> List[Dict[str, Any]]:
    """
    Searches the Danbooru tag database for tags matching a query. 
    Returns a unified list prioritized by substring matches then similarity.
    """
    global _TAG_CACHE
    
    # Normalize query: treat spaces and underscores as identical
    query = args.get("query", "").lower().replace(" ", "_")
    if not query:
        raise ToolError("The 'query' argument is required.")

    # Lazy-load the tags into memory
    if _TAG_CACHE is None:
        _TAG_CACHE = _load_tags()

    # 1. Find Substring/Alias Matches (High Priority)
    substring_matches = []
    for tag in _TAG_CACHE:
        is_direct = query in tag["name_lower"]
        is_alias = any(query in alias.strip() for alias in tag["aliases"])
        
        if is_direct or is_alias:
            # Find which alias actually matched for reporting
            matched_alias = ""
            if not is_direct:
                matched_alias = next((a.strip() for a in tag["aliases"] if query in a.strip()), query)
            
            substring_matches.append({
                "tag": tag,
                "matched_via": "name" if is_direct else "alias",
                "alias_match": matched_alias
            })
    
    # Sort substring matches by count descending
    substring_matches.sort(key=lambda x: x["tag"]["count"], reverse=True)

    # 2. Find Similarity Matches (Low Priority)
    all_names_lower = [t["name_lower"] for t in _TAG_CACHE]
    similar_names_lower = difflib.get_close_matches(query, all_names_lower, n=config.TAG_SEARCH_LIMIT, cutoff=0.5)
    similar_matches = []
    for s_lower in similar_names_lower:
        tag = next((t for t in _TAG_CACHE if t["name_lower"] == s_lower), None)
        if tag:
            similar_matches.append(tag)

    # Build Unified List
    final_results = []
    
    # Add substring matches first
    for m in substring_matches:
        final_results.append(m)
        if len(final_results) >= config.TAG_SEARCH_LIMIT:
            break
            
    # Fill remaining slots with similar matches
    if len(final_results) < config.TAG_SEARCH_LIMIT:
        mentioned_names = {m["tag"]["name_lower"] for m in final_results}
        for tag in similar_matches:
            if tag["name_lower"] not in mentioned_names:
                final_results.append({"tag": tag, "matched_via": "similarity", "alias_match": ""})
                if len(final_results) >= config.TAG_SEARCH_LIMIT:
                    break

    if not final_results:
        return [{"type": "text", "text": f"No tags found matching '{query}'."}]

    # Format output lines
    output_lines = []
    
    # Special Case: Exact Match Wiki Header
    # Check if the very first result is an exact match
    first_res = final_results[0]
    if first_res["tag"]["name_lower"] == query:
        exact_tag = first_res["tag"]
        count_fmt = format_count(str(exact_tag["count"]))
        type_sfx = get_type_suffix(exact_tag["type"])
        output_lines.append(f"Exact Match: {exact_tag['name']} ({count_fmt}){type_sfx}")
        
        wiki_info = await _fetch_wiki_info(exact_tag["name"])
        if wiki_info:
            output_lines.append(f"Wiki: {wiki_info}\n")
        else:
            output_lines.append("") # spacer
            
    # List the tags
    for res in final_results:
        tag = res["tag"]
        count_fmt = format_count(str(tag["count"]))
        type_sfx = get_type_suffix(tag["type"])
        
        # If this is the exact match we already listed in the header, skip it
        if first_res["tag"]["name_lower"] == query and tag["name_lower"] == query:
            continue

        if res["matched_via"] == "alias":
            line = f"- {res['alias_match']} → {tag['name']} ({count_fmt}){type_sfx}"
        else:
            line = f"- {tag['name']} ({count_fmt}){type_sfx}"
        
        output_lines.append(line)

    return [{"type": "text", "text": "\n".join(output_lines)}]
