import os
import asyncio
import importlib
import requests
import glob
import sys
import types
import traceback
import re
import malcat
from intelligence.base import *
from intelligence_check import get_checkers, CHECKER_TIMEOUT




async def download_poll(query, todo):
    import hashlib
    loop = asyncio.get_event_loop()
    checkers = []
    checkers_tasks = []
    res = []
    for c, options in todo.items():
        checker = c(**options)
        if options.get("enable", True):
            checkers.append(c)
            task = loop.run_in_executor(None, checker.download_and_verify, query)
            checkers_tasks.append(asyncio.wait_for(task, CHECKER_TIMEOUT))
    all_returns = await asyncio.gather(*checkers_tasks, return_exceptions=True)
    for checker, check in zip(checkers, all_returns):
        if check is None:
            res.append((checker, OnlineFile(status=CheckStatus.NOTFOUND)))
        elif isinstance(check, TimeoutError) or isinstance(check, asyncio.exceptions.TimeoutError):
            res.append((checker, OnlineFile(status=CheckStatus.TIMEOUT)))
        elif isinstance(check, NotImplementedError):
            pass
        elif isinstance(check, BaseException):
            message = f"[{checker.name}] " + "\n".join(map(str.strip, traceback.format_exception_only(check.__class__, check)))
            res.append((checker, OnlineFile(status=CheckStatus.ERROR, message=message)))
        elif isinstance(check, OnlineFile):
            res.append((checker, check))
        else:
            res.append((checker, OnlineFile(status=CheckStatus.NOTFOUND, message=str(check))))
    return sorted(res, key=lambda x: x[1].status)



def download(what, options):
    if sys.platform == "win32" and sys.version_info >= (3, 8, 0):
        asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
    what = what.strip()
    if re.match(r"^file://", what) or os.path.exists(what):
        if what.startswith("file://"):
            what = what[7:]
        with open(what, "rb") as f:
            return f.read(), os.path.basename(what), ""
    elif re.match(r"^(?:https?|hxxps?|ftp)://", what):
        from intelligence.utils import WebDownloader
        if what.startswith("hxxp"):
            what = "http" + what[4:]
        what = what.replace("[.]", ".")
        checkers_classes = [WebDownloader]
    elif re.fullmatch(r"[a-fA-F0-9]{32}|[a-fA-F0-9]{40}|[a-fA-F0-9]{64}", what):
        checkers_classes = get_checkers(malcat.env.datadir, malcat.env.userdir)
    else:
        raise ValueError("Invalid search query: please enter a http url, a file path OR a MD5/sha1/sha256 hash")
    results = asyncio.run(download_poll(what, {c: options.get(c.name, {}) for c in checkers_classes} ))
    file_content = b""
    file_name = ""
    errors = []
    checkers_positive = []
    status = ""
    for checker, result in results:
        if result.status == CheckStatus.OK:
            if not file_content:
                file_content = result.content
                file_name = f"{what}.{checker.name}"
            checkers_positive.append(checker.name)
        elif result.message:
            errors.append(result.message)
    if file_content:
        status = ""#f"\u2713 File found ({'+'.join(checkers_positive)}): {len(file_content)} bytes. What should we do?"
    else:
        status = "No file could be found using {}".format("+".join([x[0].name for x in results]))
        if errors:
            status += "\nErrors:\n{}".format("\n".join(errors))
    return file_content, file_name, status
