import time
import datetime
import tomllib
from pathlib import Path
from typing import List, Dict, Any, Optional


class ToolError(Exception):
    """Custom exception for tool-related errors to be caught by the MCP server."""
    pass

def load_toml(path: str) -> Dict[str, Any]:
    """Loads a TOML file into a dictionary."""
    try:
        with open(path, "rb") as f:
            return tomllib.load(f)
    except Exception as e:
        raise ToolError(f"Failed to load config file {path}: {str(e)}")

def format_relative_time(timestamp: float) -> str:
    """Converts a timestamp to a human-readable relative format."""
    now = time.time()
    diff = now - timestamp
    seconds = int(diff)
    
    if seconds < 0:
        return "In the future"
    if seconds < 60:
        return "Just now"
    
    minutes = seconds // 60
    if minutes < 60:
        return f"{minutes}m ago"
    
    hours = minutes // 60
    if hours < 24:
        return f"{hours}h ago"
    
    days = hours // 24
    if days == 1:
        return "Yesterday"
    if days < 30:
        return f"{days}d ago"
    
    months = days // 30
    if months < 12:
        return f"{months}mo ago"
    
    dt = datetime.datetime.fromtimestamp(timestamp)
    return dt.strftime('%Y-%m-%d')


def get_file_info_list(path: Path, extensions: Optional[tuple] = None) -> List[Dict[str, Any]]:
    """
    Gathers basic information about files in a directory.
    Optional extensions filter (e.g. ('.png', '.jpg')).
    """
    items = []
    try:
        for entry in path.iterdir():
            if extensions and not entry.name.lower().endswith(extensions):
                continue
                
            stats = entry.stat()
            items.append({
                "name": entry.name,
                "path": entry,
                "mtime": stats.st_mtime,
                "size": stats.st_size,
                "is_dir": entry.is_dir()
            })
    except Exception as e:
        raise ToolError(f"Error accessing directory {path}: {e}")
    
    return items

def sort_file_list(items: List[Dict[str, Any]], sort_by: str) -> List[Dict[str, Any]]:
    """Sorts items based on the provided key. Directories always come first."""
    if sort_by == "mtime":
        # Newest first
        items.sort(key=lambda x: (not x["is_dir"], -x["mtime"]))
    elif sort_by == "size":
        # Largest first
        items.sort(key=lambda x: (not x["is_dir"], -x["size"]))
    else:
        # Alphabetical
        items.sort(key=lambda x: (not x["is_dir"], x["name"].lower()))
    return items

def get_paginated_list(items: List[Any], page: int, page_size: int) -> List[Any]:
    """Returns a slice of the list based on page and page_size."""
    start_idx = (page - 1) * page_size
    end_idx = start_idx + page_size
    return items[start_idx:end_idx]

def parse_indices(indices_str: str) -> List[int]:
    """
    Parses a string like '11, 31-33, 44' into a list of 1-based indices.
    Preserves the order specified in the string.
    """
    indices = []
    parts = [p.strip() for p in indices_str.split(',')]
    for part in parts:
        if not part:
            continue
        if '-' in part:
            try:
                start, end = map(int, part.split('-'))
                step = 1 if start <= end else -1
                for i in range(start, end + step, step):
                    indices.append(i)
            except ValueError:
                raise ToolError(f"Invalid range format: {part}")
        else:
            try:
                indices.append(int(part))
            except ValueError:
                raise ToolError(f"Invalid index format: {part}")
    return indices

def format_count(count_str: str) -> str:
    """Formats large numbers into a human-readable string (e.g., 1.2M, 218k)."""
    try:
        count = int(count_str)
        if count >= 1_000_000:
            return f"{count / 1_000_000:.1f}M".replace(".0", "")
        if count >= 1_000:
            return f"{count / 1_000:.0f}k"
        return str(count)
    except (ValueError, TypeError):
        return "0"

def get_type_suffix(type_val: str) -> str:
    """Returns the human-readable suffix for the tag type."""
    mapping = {
        "0": "", # General
        "1": " [Artist]",
        "3": " [Copyright]",
        "4": " [Character]",
        "5": " [Meta]"
    }
    return mapping.get(str(type_val), f" [Type {type_val}]")
