import json
import time
from concurrent import futures

import grpc

from snailjob.grpc import snailjob_pb2, snailjob_pb2_grpc
from snailjob.log import SnailLog
from snailjob.metrics import get_metrics
from snailjob.schemas import DispatchJobRequest, StopJobRequest

SLEEP_SECONDS = 60


class SnailJobServicer(snailjob_pb2_grpc.UnaryRequestServicer):
    def unaryRequest(
        self,
        request: snailjob_pb2.GrpcSnailJobRequest,
        _: grpc.RpcContext,
    ) -> snailjob_pb2.GrpcResult:
        # 监控打点
        metrics = get_metrics()
        metrics.record_rpc_request()

        if request.metadata.uri == "/job/dispatch/v1":
            return SnailJobServicer.dispatch(request)
        elif request.metadata.uri == "/job/stop/v1":
            return SnailJobServicer.stop(request)
        elif request.metadata.uri == "/sync/node/metadata":
            return SnailJobServicer.registry_node_metadata(request)
        else:
            pass

    @staticmethod
    def dispatch(request: snailjob_pb2.GrpcSnailJobRequest):
        from .. import ExecutorManager

        args = json.loads(request.body)
        dispatchJobRequest = DispatchJobRequest(**args[0])
        result = ExecutorManager.dispatch(dispatchJobRequest)
        return snailjob_pb2.GrpcResult(
            reqId=request.reqId,
            status=result.status,
            message=result.message,
            data=json.dumps(result.data),
        )

    @staticmethod
    def stop(request: snailjob_pb2.GrpcSnailJobRequest):
        from .. import ExecutorManager

        args = json.loads(request.body)
        stopJobRequest = StopJobRequest(**args[0])
        ExecutorManager.stop(stopJobRequest)
        return snailjob_pb2.GrpcResult(
            reqId=request.reqId,
            status=1,
            message="",
            data="true",
        )

    @staticmethod
    def registry_node_metadata(request: snailjob_pb2.GrpcSnailJobRequest):
        from .. import ExecutorManager
        ExecutorManager.registry_node_metadata_to_server()
        return snailjob_pb2.GrpcResult(
            reqId=request.reqId,
            status=1,
            message="",
            data="true",
        )


def run_grpc_server(port: int):
    """运行客户端服务器

    Args:
        host (str): 主机 (IP, 域名)
        port (int): 服务端口
    """
    # 创建线程池执行器
    thread_pool = futures.ThreadPoolExecutor(thread_name_prefix="snail-job-server", max_workers=10)
    
    server = grpc.server(thread_pool)

    # todo 这里添加 GRPC服务端线程池 监控
    #     GRPC_SERVER_POOL_ACTIVE("grpc_server_pool_active", new String[]{}, MetricsType.GAUGE),
    #     GRPC_SERVER_POOL_SIZE("grpc_server_pool_size", new String[]{}, MetricsType.GAUGE),
    #     GRPC_SERVER_POOL_QUEUE_SIZE("grpc_server_pool_queue_size", new String[]{}, MetricsType.GAUGE),
    _start_grpc_server_pool_monitoring(thread_pool)

    snailjob_pb2_grpc.add_UnaryRequestServicer_to_server(SnailJobServicer(), server)
    server.add_insecure_port(f"0.0.0.0:{port}")
    server.start()
    try:
        while True:
            time.sleep(SLEEP_SECONDS)
    except KeyboardInterrupt:
        SnailLog.LOCAL.info("KeyboardInterrupt, 退出程序")
        server.stop(0)


def _start_grpc_server_pool_monitoring(thread_pool):
    """启动gRPC服务器线程池监控"""
    import threading
    
    def monitor_grpc_server_pool():
        """监控gRPC服务器线程池"""
        while True:
            try:
                metrics = get_metrics()
                
                # 获取线程池状态
                active_threads = len(thread_pool._threads) if hasattr(thread_pool, '_threads') else 0
                max_workers = thread_pool._max_workers if hasattr(thread_pool, '_max_workers') else 0
                queue_size = thread_pool._work_queue.qsize() if hasattr(thread_pool, '_work_queue') else 0
                
                # 更新gRPC服务器线程池指标
                metrics.set_grpc_server_pool_metrics(active_threads, max_workers, queue_size)
                
                time.sleep(5)  # 每5秒更新一次
            except Exception as e:
                SnailLog.LOCAL.debug(f"gRPC server pool monitoring error: {e}")
                time.sleep(10)  # 出错时延长间隔
    
    # 启动监控线程
    monitor_thread = threading.Thread(target=monitor_grpc_server_pool, daemon=True, name="grpc-server-pool-monitor")
    monitor_thread.start()
