"""
Middleware for admin access control.
Reads admin IDs from admins.txt or admins.json file.
"""
import json
import logging
import os
from pathlib import Path
from typing import Callable, Awaitable, Any, Set, Union
from aiogram import BaseMiddleware
from aiogram.types import Message, CallbackQuery

logger = logging.getLogger(__name__)

# Cache for admin IDs
_admin_ids: Set[str] = set()
_admin_file_path: Path = None


def _load_admin_ids() -> Set[str]:
    """
    Load admin IDs from admins.txt or admins.json file.
    Tries admins.json first, then admins.txt.
    Returns set of admin IDs as strings.
    """
    global _admin_ids, _admin_file_path
    
    # Try to find admins file in project root
    project_root = Path(__file__).parent.parent.parent.parent
    json_path = project_root / "admins.json"
    txt_path = project_root / "admins.txt"
    
    admin_ids = set()
    
    # Try JSON first
    if json_path.exists():
        try:
            with open(json_path, 'r', encoding='utf-8') as f:
                data = json.load(f)
                # Support both list and dict formats
                if isinstance(data, list):
                    admin_ids = {str(uid) for uid in data}
                elif isinstance(data, dict):
                    # If dict, try 'admins' key or 'ids' key
                    ids = data.get('admins', data.get('ids', []))
                    admin_ids = {str(uid) for uid in ids}
            _admin_file_path = json_path
            logger.info(f"Loaded {len(admin_ids)} admin IDs from {json_path}")
            return admin_ids
        except Exception as e:
            logger.error(f"Error loading admins.json: {e}")
    
    # Try TXT file
    if txt_path.exists():
        try:
            with open(txt_path, 'r', encoding='utf-8') as f:
                for line in f:
                    line = line.strip()
                    if line and not line.startswith('#'):  # Skip comments
                        admin_ids.add(line)
            _admin_file_path = txt_path
            logger.info(f"Loaded {len(admin_ids)} admin IDs from {txt_path}")
            return admin_ids
        except Exception as e:
            logger.error(f"Error loading admins.txt: {e}")
    
    logger.warning("No admins file found (admins.json or admins.txt). Admin access disabled.")
    return admin_ids


def is_admin(user_id: Union[int, str]) -> bool:
    """
    Check if user ID is in admin list.
    Automatically loads admin IDs on first call.
    """
    global _admin_ids
    
    if not _admin_ids:
        _admin_ids = _load_admin_ids()
    
    user_id_str = str(user_id)
    return user_id_str in _admin_ids


def reload_admin_ids() -> None:
    """Force reload admin IDs from file."""
    global _admin_ids
    _admin_ids = _load_admin_ids()


class AdminMiddleware(BaseMiddleware):
    """
    Middleware that restricts access to admin-only handlers.
    Only allows users whose IDs are in admins.txt or admins.json.
    """
    
    def __init__(self, allow_non_admins: bool = False):
        """
        Args:
            allow_non_admins: If True, allows non-admins but logs warning. Default False.
        """
        self.allow_non_admins = allow_non_admins
        # Load admin IDs on initialization
        _load_admin_ids()
    
    async def __call__(
        self,
        handler: Callable[[Union[Message, CallbackQuery], dict], Awaitable[Any]],
        event: Union[Message, CallbackQuery],
        data: dict,
    ) -> Any:
        # Extract user ID
        if isinstance(event, Message):
            user_id = str(event.from_user.id) if event.from_user else None
        elif isinstance(event, CallbackQuery):
            user_id = str(event.from_user.id) if event.from_user else None
        else:
            return await handler(event, data)
        
        if not user_id:
            if isinstance(event, Message):
                await event.answer("Ошибка: не удалось определить пользователя.")
            elif isinstance(event, CallbackQuery):
                await event.answer("Ошибка: не удалось определить пользователя.", show_alert=True)
            return
        
        # Check if user is admin
        if not is_admin(user_id):
            if self.allow_non_admins:
                logger.warning(f"Non-admin user {user_id} attempted admin action")
                return await handler(event, data)
            else:
                logger.warning(f"Access denied for non-admin user {user_id}")
                if isinstance(event, Message):
                    await event.answer("❌ Доступ запрещён. Эта команда доступна только администраторам.")
                elif isinstance(event, CallbackQuery):
                    await event.answer("❌ Доступ запрещён. Эта команда доступна только администраторам.", show_alert=True)
                return
        
        # User is admin, proceed
        return await handler(event, data)

