import time
import threading
from threading import Thread
from importlib import import_module

from cobweb import log, Queue, DBItem, RedisDB, OssDB, StorerInterface
from cobweb.utils import struct_queue_name, restore_table_name
from .models import Scheduler, Spider, Storer
# from collections import namedtuple

# def start_seeds(seeds):
#     if not seeds:
#         return None
#     if any(isinstance(seeds, t) for t in (list, tuple)):
#         return [Seed(seed) for seed in seeds]
#     elif any(isinstance(seeds, t) for t in (str, dict)):
#         return Seed(seeds)


# def parse_storer_info(storer_info):
#     storer_data = {}
#     storer_info_list = []
#     if storer_info.__class__.__name__ == 'StorerInfo':
#         storer_info_list.append(storer_info)
#     elif any(isinstance(storer_info, t) for t in (list, tuple)):
#         storer_info_list = storer_info
#     for info in storer_info_list:
#         db_name = info.DB.__name__
#         storer_data.setdefault(db_name, {"StorerDB": info.DB, "db_args_list": []})
#         storer_data[db_name]["db_args_list"].append(info[1:])
#     return storer_data

def get_scheduler_db(db):
    if isinstance(db, str):
        if "." in db:
            model_path = db.split(".")
            model = import_module(db)
            obj = getattr(model, db)
        else:
            model = import_module(f"cobweb.db.scheduler.{db.lower()}")
            obj = getattr(model, db.capitalize())
        return obj
        # if db.lower() in dir(StorerDB):
        #     return getattr(StorerDB, db)
        # else:
        #     pass
    elif issubclass(db, StorerInterface):
        return db
    raise TypeError()


def get_storer_db(db):
    if isinstance(db, str):
        if "." in db:
            model_path = db.split(".")
            model = import_module(db)
            obj = getattr(model, db)
        else:
            model = import_module(f"cobweb.db.storer.{db.lower()}")
            obj = getattr(model, db.capitalize())
        return obj, db.lower()
        # if db.lower() in dir(StorerDB):
        #     return getattr(StorerDB, db)
        # else:
        #     pass
    elif issubclass(db, StorerInterface):
        return db, db.__name__.lower()
    raise TypeError()


def check(stop, last, spider, scheduler, storer_list, ready_seed_length, spider_queue_length):
    time.sleep(30)
    spider_info = """
----------------------- check -----------------------
            running_spider_thread_num: {0}
            redis_ready_seed_length:   {1}
            redis_spider_seed_length:  {2}
            memory_seed_queue_length:  {3}
            storer_upload_queue_length_info: 
                {4}
-----------------------  end  -----------------------"""
    while True:

        running_spider_thread_num = spider.spider_in_progress.length
        redis_ready_seed_length = ready_seed_length()
        redis_spider_seed_length = spider_queue_length()
        memory_seed_queue_length = scheduler.queue.length
        storer_upload_queue_list = []
        storer_queue_empty = True
        for storer in storer_list:
            if storer.queue.length:
                storer_queue_empty = False
            storer_upload_queue_list.append(
                f"{storer.__class__.__name__}: {storer.queue.length}"
            )
        storer_upload_queue_length_info = "\n            ".join(storer_upload_queue_list)

        if (
                scheduler.stop and
                not redis_ready_seed_length and
                not memory_seed_queue_length and
                not running_spider_thread_num
        ):
            log.info("spider is done?")
            last.set()
            if storer_queue_empty and not redis_spider_seed_length:
                log.info("spider done!")
                break
        last.clear()

        log.info(spider_info.format(
            running_spider_thread_num,
            redis_ready_seed_length,
            redis_spider_seed_length,
            memory_seed_queue_length,
            storer_upload_queue_length_info
        ))

        time.sleep(3)
    stop.set()


def launcher(task):
    """
    任务启动装饰器
    :param task: 任务配置信息
    """
    def decorator(func):
        """
        Item:
            Textfile()
            Loghub()
            Console()
        e.g.
        task.fields = "a,b"
        func(item, seed)
            a = "a"
            b = "b"
            data = {"a": "a", "b": "b"}
            yield item.Loghub(**data)
            yield item.Loghub(a=a, b=b)
        """
        storer_list = []

        # 程序结束事件
        last = threading.Event()
        # 停止采集事件
        stop = threading.Event()

        # 初始化redis信息
        redis_db = RedisDB(task.project, task.task_name, task.redis_info)

        log.info("初始化cobweb!")

        seed_queue = Queue()

        if task.scheduler_info is None:
            task.scheduler_info = dict()

        # 调度器动态继承
        sql = task.scheduler_info.get("sql")
        table = task.scheduler_info.get("table")
        size = task.scheduler_info.get("size")
        scheduler_config = task.scheduler_info.get("config")
        scheduler_db = task.scheduler_info.get("db", "default")
        DB = get_scheduler_db(scheduler_db)
        # SchedulerDB, table, sql, length, size, config = task.scheduler_info
        SchedulerTmp = type(DB.__name__, (Scheduler, DB), {})

        # 初始化调度器
        scheduler = SchedulerTmp(
            table=table, sql=sql, size=size, queue=seed_queue,
            length=task.scheduler_queue_length, config=scheduler_config
        )

        # 初始化采集器
        spider = Spider(seed_queue, task.max_retries)

        # 解析存储器信息
        storer_info_list = task.storer_info
        if not isinstance(storer_info_list, list):
            storer_info_list = [storer_info_list]

        # new item
        item = type("Item", (object,), {"redis_client": redis_db})()

        if task.oss_config:
            item.oss = OssDB(**task.oss_config)

        for storer_info in storer_info_list:
            storer_db = storer_info["db"]
            fields = storer_info["fields"]
            storer_table = storer_info.get("table", "console")
            storer_config = storer_info.get("config")

            StorerDB, db_name = get_storer_db(storer_db)
            StorerTmp = type(StorerDB.__name__, (Storer, StorerDB), {})

            if not getattr(item, db_name, None):
                instance = type(db_name, (DBItem,), {})
                setattr(item, db_name, instance)

            storer_item_instance = getattr(item, db_name)
            storer_item_instance.init_item(storer_table, fields)

            storer_queue = struct_queue_name(db_name, storer_table)
            queue = getattr(storer_item_instance, storer_queue)
            # 初始话存储器
            table_name = restore_table_name(table_name=storer_table)
            storer = StorerTmp(
                table=table_name, fields=fields,
                length=task.storer_queue_length,
                queue=queue, config=storer_config
            )
            storer_list.append(storer)

        # for db_name in storer_data.keys():
        #     # 存储器动态继承
        #     StorerDB = storer_data[db_name]["StorerDB"]
        #     StorerTmp = type(StorerDB.__name__, (Storer, StorerDB), {})
        #     db_args_list = storer_data[db_name]["db_args_list"]
        #     for storer_db_args in db_args_list:
        #         table, fields, length, config = storer_db_args
        #         if not getattr(item, db_name, None):
        #             instance = type(db_name, (DBItem,), {})
        #             setattr(item, db_name, instance)
        #         # 创建存储xxx, 创建存储队列
        #         storer_item_instance = getattr(item, db_name)
        #         storer_item_instance.init_item(table, fields)
        #         #
        #         storer_queue = struct_queue_name(db_name, table)
        #         queue = getattr(storer_item_instance, storer_queue)
        #         # 初始话存储器
        #         table_name = restore_table_name(table_name=table)
        #         storer = StorerTmp(table_name, fields, length, queue, config)
        #         storer_list.append(storer)

        Thread(target=redis_db.check_spider_queue, args=(stop, len(storer_list))).start()
        Thread(target=redis_db.set_heartbeat, args=(stop,)).start()

        # 推送初始种子
        # seeds = start_seeds(task.start_seed)
        redis_db.add_seed(task.seeds)
        # 启动调度器, 调度至redis队列
        Thread(
            # name="xxxx_schedule_seeds",
            target=scheduler.schedule_seed,
            args=(
                redis_db.ready_seed_length,
                redis_db.get_scheduler_lock,
                redis_db.add_seed
            )
        ).start()

        # 启动调度器, 调度任务队列
        Thread(
            # name="xxxx_schedule_task",
            target=scheduler.schedule_task,
            args=(
                stop, redis_db.get_seed,
                redis_db.ready_seed_length
            )
        ).start()

        # 启动采集器
        for index in range(task.spider_num):
            Thread(
                # name=f"xxxx_spider_task:{index}",
                target=spider.spider_task,
                args=(
                    stop, func, item,
                    redis_db.del_seed
                )
            ).start()

        # 启动存储器
        for storer in storer_list:
            Thread(
                # name=f"xxxx_store_task:{storer.table}",
                target=storer.store_task,
                args=(
                    stop, last,
                    redis_db.reset_seed,
                    redis_db.set_storer
                )
            ).start()

        Thread(
            # name="check_spider",
            target=check,
            args=(
                stop, last, spider,
                scheduler, storer_list,
                redis_db.ready_seed_length,
                redis_db.spider_queue_length,
            )
        ).start()

    return decorator


# model = get_storer_db("console")
# print()