# Copyright (c) 2025, JINGROW and contributors
# For license information, please see license.txt

"""
Jingrow whitelist routing service
"""

from fastapi import APIRouter, Request, HTTPException
from fastapi.responses import JSONResponse
import importlib
from typing import Any, Dict
import logging
from jingrow import get_whitelisted_function
from jingrow.utils.auth import get_jingrow_api_headers, set_request_session_cookie
from jingrow.utils.jingrow_api import get_logged_user
from jingrow.utils.app_manager import ensure_apps_on_sys_path

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/action")

async def authenticate_request(request: Request, allow_guest: bool) -> bool:
    """
    Authenticate request. Supports:
    1. Session Cookie authentication
    2. API Key authentication
    """
    if allow_guest:
        return True
    
    # Method 1: Check Session Cookie authentication
    session_cookie = request.cookies.get('sid')
    if session_cookie:
        try:
            user = get_logged_user(session_cookie)
            if user:
                # Store session cookie in ContextVar for downstream use
                set_request_session_cookie(session_cookie)
                logger.info(f"Session authenticated: {user}")
                return True
        except Exception as e:
            logger.warning(f"Session authentication failed: {e}")
    
    # Method 2: Check API Key authentication
    auth_header = request.headers.get('Authorization')
    if auth_header and auth_header.startswith('token '):
        try:
            token_part = auth_header[6:]
            if ':' in token_part:
                api_key, api_secret = token_part.split(':', 1)
                expected_headers = get_jingrow_api_headers()
                if expected_headers and expected_headers.get('Authorization') == auth_header:
                    logger.info("API Key authenticated")
                    return True
        except Exception as e:
            logger.warning(f"API Key authentication failed: {e}")
    
    logger.warning("Authentication failed: no valid credentials")
    return False

async def _process_whitelist_call(request: Request, full_module_path: str):
    """通用处理：接收完整点分路径 '<package.module.function>' 并执行调用"""
    try:
        # 确保 apps 目录在 sys.path 中（支持跨 app 导入）
        ensure_apps_on_sys_path()
        
        # 解析路径并导入模块
        modulename = ".".join(full_module_path.split('.')[:-1])
        methodname = full_module_path.split('.')[-1]
        module = import_module(modulename)
        func = getattr(module, methodname)
        
        # 检查白名单（使用实际导入后的模块名，因为装饰器注册时使用 func.__module__）
        actual_whitelist_path = f"{module.__name__}.{methodname}"
        whitelist_info = get_whitelisted_function(actual_whitelist_path)
        
        # 如果函数不在白名单中，返回404（就像路由不存在一样）
        if not whitelist_info:
            logger.debug(f"函数未在白名单中，返回404: {actual_whitelist_path}")
            raise HTTPException(status_code=404, detail="Not Found")
        
        # 检查 HTTP 方法
        if request.method not in whitelist_info['methods']:
            raise HTTPException(status_code=405, detail=f"Method {request.method} not allowed")
        
        # 检查权限
        if not whitelist_info['allow_guest']:
            if not await authenticate_request(request, whitelist_info['allow_guest']):
                raise HTTPException(status_code=401, detail="Authentication required")
        
        # 根据请求类型获取数据
        if request.method == 'GET':
            request_data = dict(request.query_params)
        else:
            # 检查 Content-Type 是否为 multipart/form-data
            content_type = request.headers.get('content-type', '')
            is_multipart = 'multipart/form-data' in content_type
            
            if is_multipart:
                # 处理文件上传
                form = await request.form()
                request_data = {}
                for key, value in form.items():
                    request_data[key] = value
            else:
                # 处理 JSON body
                try:
                    request_data = await request.json()
                except Exception:
                    request_data = {}
        
        result = func(**request_data)
        
        return JSONResponse(content={"message": result})
    
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Request handler error: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.api_route("/{module_path:path}", methods=["GET", "POST", "PUT", "DELETE"])
async def handle_request(request: Request, module_path: str):
    """
    直接传入完整点分路径 '<package.module.function>'
    只处理包含点分路径的模块路径
    """
    # 验证模块路径格式：必须包含至少一个点（如 package.module.function）
    if '.' not in module_path:
        raise HTTPException(status_code=404, detail="Not Found")
    
    return await _process_whitelist_call(request, module_path)

def import_module(module_path: str):
    """动态导入模块"""
    try:
        return importlib.import_module(module_path)
    except ImportError as e:
        logger.error(f"Failed to import module {module_path}: {e}")
        raise HTTPException(status_code=404, detail=f"Module {module_path} not found")

