"""
Aria2 Error Parser - Parse and categorize aria2 errors with actionable solutions.

Based on official aria2 documentation:
https://aria2.github.io/manual/en/html/aria2c.html
"""

from typing import Optional, Dict, Any
from enum import Enum
import re


class Aria2ErrorCategory(Enum):
    """Categories of aria2 errors for better handling"""
    FILE_CONFLICT = "file_conflict"  # File exists, control file missing
    NETWORK = "network"  # Connection, timeout, DNS errors
    HTTP = "http"  # HTTP status codes
    AUTH = "auth"  # Authentication failures
    CORRUPTION = "corruption"  # Checksum, torrent parsing errors
    CONFIG = "config"  # Invalid options
    RESOURCE = "resource"  # Disk full, permission denied
    UNKNOWN = "unknown"


class Aria2Error:
    """Parsed aria2 error with category and actionable solution"""
    
    def __init__(
        self,
        category: Aria2ErrorCategory,
        original_message: str,
        user_message: str,
        suggested_action: str,
        technical_details: Optional[Dict[str, Any]] = None
    ):
        self.category = category
        self.original_message = original_message
        self.user_message = user_message
        self.suggested_action = suggested_action
        self.technical_details = technical_details or {}
    
    def to_dict(self) -> Dict[str, Any]:
        return {
            'category': self.category.value,
            'original_message': self.original_message,
            'user_message': self.user_message,
            'suggested_action': self.suggested_action,
            'technical_details': self.technical_details
        }


# Error patterns with solutions
ERROR_PATTERNS = [
    # File exists but control file missing (--allow-overwrite=false)
    {
        'pattern': r'File .+ exists, but a control file.*does not exist',
        'category': Aria2ErrorCategory.FILE_CONFLICT,
        'user_message': 'Tệp đã tồn tại từ lần tải trước (chưa hoàn thành hoặc đã hoàn thành)',
        'suggested_action': 'Xóa tệp cũ và tệp .aria2 (nếu có) để tải lại từ đầu, hoặc bỏ qua nếu tệp đã hoàn thành',
        'extract_file': lambda msg: (m.group(1) if (m := re.search(r'File (.+?) exists', msg)) else None)
    },
    
    # Connection timeout
    {
        'pattern': r'Read timed out|Connection timed out',
        'category': Aria2ErrorCategory.NETWORK,
        'user_message': 'Kết nối bị timeout (quá chậm hoặc không phản hồi)',
        'suggested_action': 'Kiểm tra kết nối mạng, giảm số connections (--max-connection-per-server), hoặc tăng timeout'
    },
    
    # Connection refused
    {
        'pattern': r'Connection refused|Could not connect',
        'category': Aria2ErrorCategory.NETWORK,
        'user_message': 'Không thể kết nối đến server',
        'suggested_action': 'Kiểm tra URL, firewall, hoặc server có thể đang offline'
    },
    
    # HTTP 404
    {
        'pattern': r'404|Not Found',
        'category': Aria2ErrorCategory.HTTP,
        'user_message': 'Tệp không tồn tại trên server (404)',
        'suggested_action': 'Link download có thể đã hết hạn hoặc tệp đã bị xóa'
    },
    
    # HTTP 403
    {
        'pattern': r'403|Forbidden',
        'category': Aria2ErrorCategory.HTTP,
        'user_message': 'Không có quyền truy cập (403)',
        'suggested_action': 'Kiểm tra cookies, session, hoặc referer header'
    },
    
    # HTTP 401
    {
        'pattern': r'401|Unauthorized',
        'category': Aria2ErrorCategory.AUTH,
        'user_message': 'Yêu cầu xác thực (401)',
        'suggested_action': 'Cần đăng nhập lại hoặc refresh token/session'
    },
    
    # Checksum mismatch
    {
        'pattern': r'Checksum validation failed|piece hash check failed',
        'category': Aria2ErrorCategory.CORRUPTION,
        'user_message': 'Dữ liệu bị lỗi (checksum không khớp)',
        'suggested_action': 'Xóa tệp .aria2 và tải lại từ đầu'
    },
    
    # Disk full
    {
        'pattern': r'No space left on device|Disk full',
        'category': Aria2ErrorCategory.RESOURCE,
        'user_message': 'Không đủ dung lượng ổ đĩa',
        'suggested_action': 'Giải phóng dung lượng hoặc thay đổi thư mục đích'
    },
    
    # Permission denied
    {
        'pattern': r'Permission denied',
        'category': Aria2ErrorCategory.RESOURCE,
        'user_message': 'Không có quyền ghi file',
        'suggested_action': 'Kiểm tra quyền truy cập thư mục đích'
    },
    
    # Too many redirects
    {
        'pattern': r'Too many redirects',
        'category': Aria2ErrorCategory.HTTP,
        'user_message': 'Quá nhiều lần chuyển hướng',
        'suggested_action': 'URL có thể bị lỗi hoặc redirect loop'
    }
]


def parse_aria2_error(error_message: str) -> Aria2Error:
    """
    Parse aria2 error message and return structured error with solution.
    
    Args:
        error_message: Raw error message from aria2
        
    Returns:
        Aria2Error object with category and suggested action
    """
    # Try to match known patterns
    for pattern_info in ERROR_PATTERNS:
        if re.search(pattern_info['pattern'], error_message, re.IGNORECASE):
            technical_details = {}
            
            # Extract file path if available
            if 'extract_file' in pattern_info:
                file_path = pattern_info['extract_file'](error_message)
                if file_path:
                    technical_details['file_path'] = file_path
            
            return Aria2Error(
                category=pattern_info['category'],
                original_message=error_message,
                user_message=pattern_info['user_message'],
                suggested_action=pattern_info['suggested_action'],
                technical_details=technical_details
            )
    
    # Unknown error - return generic error
    return Aria2Error(
        category=Aria2ErrorCategory.UNKNOWN,
        original_message=error_message,
        user_message='Lỗi không xác định từ aria2',
        suggested_action='Xem log chi tiết để biết thêm thông tin',
        technical_details={'raw_error': error_message}
    )


def format_error_for_user(error: Aria2Error, include_technical: bool = False) -> str:
    """
    Format error message for user display.
    
    Args:
        error: Parsed Aria2Error object
        include_technical: Whether to include technical details
        
    Returns:
        Formatted error string
    """
    message = f"❌ {error.user_message}\n"
    message += f"💡 Giải pháp: {error.suggested_action}"
    
    if include_technical:
        message += f"\n🔧 Chi tiết kỹ thuật: {error.original_message}"
        if error.technical_details:
            for key, value in error.technical_details.items():
                message += f"\n   - {key}: {value}"
    
    return message
