import logging
import shutil
import subprocess
import sys
import time
import urllib
from pathlib import Path

import requests
from elevate import elevate
from util import Config, compress_files, get_list_of_files, setup_monitoring_folder


def static_analysis(api, file_path, name, timestamp):
    logging.info("Sending files for static-analysis")
    if not file_path.exists():
        logging.warning(f"file doesn't exist {file_path}")
        return False
    else:
        with open(file_path, "rb") as f:
            r = requests.post(
                urllib.parse.urljoin(api, "static-analysis"),
                data={"name": str(name), "timestamp": timestamp},
                files={"file": f},
            )
            if r.ok:
                logging.debug(f"Uploaded {file_path}")
                logging.info("Static analysis completed")
                response = r.json()
                logging.debug(response)
                if response["status"] != "ok":
                    return False
                return response
            else:
                logging.warning(f"Failed on {file_path}")
                return False


def parse_dumps(api, file_path, name, timestamp):
    logging.info("Parsing dumps...")
    if not file_path.exists():
        logging.warning(f"file doesn't exist {file_path}")
        return False
    else:
        with open(file_path, "rb") as f:
            r = requests.post(
                urllib.parse.urljoin(api, "parse-dumps"),
                data={"name": str(name), "timestamp": timestamp},
                files={"file": f},
            )
            if r.ok:
                logging.debug(f"Uploaded {file_path}")
                logging.info("Parsing dumps completed")
                response = r.json()
                logging.debug(response)
                if response["status"] != "ok":
                    return False
                return response
            else:
                logging.warning(f"Failed on {file_path}")
                return False


def mapping(api, name, timestamp):
    r = requests.post(
        urllib.parse.urljoin(api, "mapping"),
        data={"name": str(name), "timestamp": timestamp},
    )
    if r.ok:
        logging.info("Mapping completed")
        response = r.json()
        logging.debug(response)
        if response["status"] != "ok":
            return False
        return response


"""
usage: python3 client.py <path-to-malware>(optional)
"""
if __name__ == "__main__":
    # ask for admin privileges
    elevate()

    logging.basicConfig(
        level=logging.DEBUG,
        format="[%(asctime)s][%(levelname)s] %(message)s",
        handlers=[logging.StreamHandler()],
    )

    # import configs
    config = Config.get_config()
    log_dir = config.log_dir / "Monitoring"
    exe_path = config.exe_path.absolute().resolve()

    if log_dir.exists() and not log_dir.is_dir():
        logging.error("config.ini: LOG_DIR path is not a directory")
        sys.exit(1)

    base_cmd = [str(exe_path)]
    logging.debug(f"sys.argv: {sys.argv}")

    # denotes if realtime will be done continuosly or not
    realtime = True
    malware_file = None
    malware_file_name = None

    # if second argument is given, process that
    if len(sys.argv) == 2:
        malware_file = Path(sys.argv[1]).absolute().resolve()
        malware_file_name = malware_file.parts[-1]
        realtime = False

    logging.debug(f"malware_file: {malware_file}")
    logging.debug(f"malware_file_name: {malware_file_name}")

    while True:
        # setup the monitoring folder
        cur_monitoring_folder, timestamp = setup_monitoring_folder(
            log_dir, malware_file_name
        )

        # generate the command to execute
        new_cmd = base_cmd + [str(cur_monitoring_folder)]

        if malware_file:
            new_cmd += [str(malware_file)]

        logging.info(f"Running command {new_cmd}")
        try:
            subprocess.run(new_cmd)
        except KeyboardInterrupt:
            logging.info("Cleaning up corrupted logs")
            shutil.rmtree(cur_monitoring_folder)
            sys.exit(1)

        log_files = [cur_monitoring_folder / i for i in get_list_of_files()]

        zip_file = compress_files(log_files)
        response = parse_dumps(config.api_url, zip_file, malware_file_name, timestamp)
        zip_file.unlink()

        if response and len(response["files"]) > 0:
            logging.info("Got a list of files to send for static analysis")

            zip_file = compress_files(map(Path, response["files"]))
            response = static_analysis(
                config.api_url, zip_file, malware_file_name, timestamp
            )
            zip_file.unlink()

        # mapping
        if response:
            response = mapping(config.api_url, malware_file_name, timestamp)
            logging.info(f'URL: {response["url"]}')

        if config.auto_del:
            logging.info(f"Deleting {cur_monitoring_folder}")
            shutil.rmtree(cur_monitoring_folder)

        if realtime:
            time.sleep(20)
        else:
            break
