import inspect
import threading as th
from typing import Generator, Callable

from textwrap import shorten

from ldnode.runner.events import TaskAnswer
from ldnode.calculator.handlers import handler_registry
from .models import OutputMessageType
from .worker_context import WorkerContext


def check_param_support(func: Callable, param_name: str) -> bool:
    """ 检查参数支持 """
    try:
        sig = inspect.signature(func)
    except (TypeError, ValueError):
        # 某些内建/扩展函数可能拿不到签名：保守处理为不支持
        return False
    params = list(sig.parameters.values())
    # 如果有 **kwargs，则我们支持关键字参数调用
    if any(p.kind == inspect.Parameter.VAR_KEYWORD for p in params):
        return True
    if param_name in sig.parameters:
        p = sig.parameters[param_name]
        if p.kind in (inspect.Parameter.KEYWORD_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD):
            return True
    return False


class CalcController(th.Thread):
    """ 计算控制器 """
    def __init__(self, context: WorkerContext):
        super().__init__(daemon=True)
        self.context = context
        
    def return_result(self, answer: TaskAnswer) -> None:
        self.context.outbox.put({
            "msg_type": OutputMessageType.TASK_ANSWER,
            "payload": answer,
        })
        
        
    def run(self) -> None:
        """ 运行控制器 """
        while self.context.running:
            # 等待任务
            task, cancel_token = self.context.wait_for_task()
            
            # 如果任务被取消，则返回错误
            if self.context.is_canceled():
                self.return_result({
                    "task_id": task['task_id'],
                    "method": task['method'],
                    "result": { "data_type": "error", "data": "任务被取消", "canceled": True },
                    "completed": True,
                })
                continue
            # 获取处理器
            try:
                if handler := handler_registry.get(task['method']):
                    _parameters = {
                        "task_id": task["task_id"],
                        **task["parameters"],
                    }
                    _other_parameters = {}
                    if check_param_support(handler.handle, "cancel_token"):
                        _other_parameters["cancel_token"] = cancel_token
                    if check_param_support(handler.handle, "invoke"):
                        _other_parameters["invoke"] = self.context.invoke
                    result = handler.handle(_parameters, **_other_parameters)
                    if isinstance(result, Generator):
                        # 返回过程结果
                        for item in result:
                            self.return_result({
                                "task_id": task['task_id'],
                                "method": task['method'],
                                "result": item,
                                "completed": False,
                            })
                        # 返回完成结果
                        self.return_result({
                            "task_id": task['task_id'],
                            "method": task['method'],
                            "result": { "canceled": self.context.is_canceled() },
                            "completed": True,
                        })
                    else:
                        # 返回完成结果
                        self.return_result({
                            "task_id": task['task_id'],
                            "method": task['method'],
                            "result": { **result, "canceled": self.context.is_canceled() },
                            "completed": True,
                        })
                else:
                    # 返回方法不存在错误
                    self.return_result({
                        "task_id": task['task_id'],
                        "method": task['method'],
                        "result": { "data_type": "error", "data": "处理器不存在" },
                        "completed": True,
                    })
            except Exception as e:
                # 返回计算异常错误结果
                self.return_result({
                    "task_id": task['task_id'],
                    "method": task['method'],
                    "result": { "data_type": "error", "data": shorten(str(e), width=255, placeholder="...") },
                    "completed": True,
                })
