'''
调度服务器
'''
import logging
import time
from datetime import datetime, timedelta

from rpyc.utils.server import ThreadedServer
from rpyc.utils.helpers import classpartial
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.executors.pool import (
    ThreadPoolExecutor,
    ProcessPoolExecutor
)
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from apscheduler.events import (
    EVENT_JOB_EXECUTED,
    EVENT_JOB_ERROR,
    EVENT_JOB_ADDED,
    EVENT_JOB_SUBMITTED,
    EVENT_JOB_REMOVED
)

from .service import SchedulerService


def event_listener(event, scheduler):
    code = getattr(event, 'code')
    job_id = getattr(event, 'job_id')
    jobstore = getattr(event, 'jobstore')
    event_codes = {
        EVENT_JOB_EXECUTED: 'JOB_EXECUTED',
        EVENT_JOB_ERROR: 'JOB_ERROR',
        EVENT_JOB_ADDED: 'JOB_ADDED',
        EVENT_JOB_SUBMITTED: 'JOB_SUBMITTED',
        EVENT_JOB_REMOVED: 'JOB_REMOVED'
    }
    event_name = None
    job = None
    need_record = False
    if code in event_codes:
        need_record = True
        event_name = event_codes[code]
        job = scheduler.get_job(job_id)
        if code == EVENT_JOB_SUBMITTED:
            time.sleep(0.05)
        elif code in [EVENT_JOB_EXECUTED, EVENT_JOB_ERROR]:
            # 执行完成和出错不可能同时出现,故延时一样
            time.sleep(0.1)
    print('*' * 80)
    print(job_id)
    print(event_name, datetime.now())
        # print(job)
    '''
    if hasattr(event, 'exception') and event.exception:
        error, *_ = event.exception.args
        tmp = re.sub(r'\([\w.]+\)\s*ORA-\d+:\s*', '', error)
        if bool(tmp):
            error = tmp
        # TODO: log error
    '''

def modify_logger(logger, log_file):
    # refer: https://docs.python.org/3.5/library/logging.html#logrecord-attributes
    formatter = logging.Formatter(
        fmt='\n'.join([
            '[%(name)s] %(asctime)s.%(msecs)d',
            '\t%(pathname)s [line: %(lineno)d]',
            '\t%(processName)s[%(process)d] => %(threadName)s[%(thread)d] => %(module)s.%(filename)s:%(funcName)s()',
            '\t%(levelname)s: %(message)s\n'
        ]),
        datefmt='%Y-%m-%d %H:%M:%S'
    )
    # stream_handler = logging.StreamHandler()
    # stream_handler.setFormatter(formatter)
    # logger.addHandler(stream_handler)

    file_handler = logging.FileHandler(log_file, mode='a', encoding='utf-8')
    file_handler.setFormatter(formatter)
    logger.addHandler(file_handler)

    logger.setLevel(logging.DEBUG)

    return logger

def get_scheduler(store_path=None, log_file=None):
    if store_path is None:
        store_path = r'jobstore.sqlite'
    if log_file is None:
        log_file = r'logger.log'
    scheduler = BackgroundScheduler({'apscheduler.timezone': 'Asia/Shanghai'})
    jobstores = {
        'default': SQLAlchemyJobStore(url='sqlite:///{0}'.format(store_path))
    }
    executors = {
        'default': ThreadPoolExecutor(20),
        'processpool': ProcessPoolExecutor(5)
    }
    job_defaults = {
        'coalesce': False,
        'max_instances': 1
    }
    scheduler.configure(jobstores=jobstores, executors=executors)
    # 事件记录
    scheduler.add_listener(
        lambda event: event_listener(event, scheduler),
        EVENT_JOB_EXECUTED | EVENT_JOB_ERROR | EVENT_JOB_ADDED | EVENT_JOB_SUBMITTED | EVENT_JOB_REMOVED
    )
    # 日志定制
    scheduler._logger = modify_logger(scheduler._logger, log_file=log_file)
    return scheduler

def get_server(listen_config, scheduler, SchedulerServiceClass=None):
    # 额外构造函数参数
    ser_args = ['test args']
    ser_kwargs = {}
    # 传递Service构造函数参数
    SSC = SchedulerServiceClass if SchedulerServiceClass is not None else SchedulerService
    service = classpartial(SSC, scheduler, *ser_args, **ser_kwargs)
    # 允许属性访问
    protocol_config = {'allow_public_attrs': True}
    # 实例化RPYC服务器
    server = ThreadedServer(service,  protocol_config=protocol_config, **listen_config)
    return server

def schedule_start(listen_config=None, store_path=None, log_file=None, SchedulerServiceClass=None):
    # 实例化调度器
    scheduler = get_scheduler(store_path=store_path, log_file=log_file)
    # 启动调度
    scheduler.start()
    # 监听配置
    if listen_config is None:
        listen_config = {
            'port': 12345,
            'hostname': '0.0.0.0'
        }
    # 实例化服务器
    server = get_server(listen_config, scheduler, SchedulerServiceClass=SchedulerServiceClass)
    print('rpyc server running at [{hostname}:{port}]'.format(**listen_config))
    try:
        # 启动RPYC服务器
        server.start()
    except (KeyboardInterrupt, SystemExit):
        pass
    finally:
        # 停止调度
        scheduler.shutdown()
        # 停止RPYC服务器
        server.close()

if __name__ == '__main__':
    schedule_start()