import os
import sys
import psutil

sys.path.insert(0, './libs.zip')

import configparser
import json

from bottle import static_file
from datetime import datetime


def read_config_file():
    """
        Reading and return main config of InfoWS
    """
    main_path = os.path.abspath(
        os.path.join(os.path.realpath(__file__), r"..\..\..")
    )
    config_file = os.path.join(main_path, "config.json")

    with open(config_file, mode='r') as conf_file:
        return {
            "app_path": main_path,
            **json.loads(conf_file.read())
        }


def load_config():
    """
        Reading and parsing api_server.ini for
        number of instances and paths
    """
    _config = read_config_file()

    _config["logging_path"] = os.path.join(_config["app_path"], "_logs")
    _config["logging_web"] = os.path.join(_config["logging_path"], "webservice")
    _config["logging_api"] = os.path.join(_config["logging_path"], "infoapi")
    _config["logging_json"] = os.path.join(_config["logging_path"], "json")

    # reading ini file from infoapi folder
    __path = os.path.join(_config["infoapi_path"], "api_server.ini")
    if os.path.exists(__path):
        config = configparser.ConfigParser()
        config.read(
            os.path.join(_config["infoapi_path"], "api_server.ini"),
            encoding="utf-8-sig"
        )

        # read log path
        if config["logging"].get("LOGGING_PATH", None):
            _config["api_log_path"] = os.path.join(_config["infoapi_path"], config["logging"].get("LOGGING_PATH").strip("'\""))

        # cheksum params
        _config["cheksum_required"] = False
        if config["main"].get("CHECKSUM_REQUIRED") == "true":
            _config["cheksum_required"] = True

        _config["cheksum_disabled"] = True
        if config["main"].get("CHECKSUM_DISABLED") == "false":
            _config["cheksum_disabled"] = False

        # reading WebAPI ini file and finding VINFO disk drive
        infosys_path = os.path.join(
            config["main"]["APP_DISK"].replace('"', ''),
            "\\infosys"
        )
        if not os.path.exists(infosys_path):
            infosys_path = os.path.join(
                config["main"]["APP_DISK"].replace('"', ''), "\\")
        _config["infosys_static"] = infosys_path

        for port in map(str, _config["infows_port"]):
            port = port.strip()
            if config.has_section(f"port{port}"):
                _config[f"infosys_static_{port}"] = os.path.join(
                    config[f"port{port}"]["APP_DISK"].replace('"', ''),
                    "\\infosys"
                )

        config.clear()
        del config

    
    # check and create folders if they do not exists
    list(
        map(
            os.mkdir,
            filter((lambda x: not os.path.isdir(x)),
                   [
                _config["logging_path"],
                _config["logging_api"],
                _config["logging_web"],
                _config["logging_json"]
            ]
            )
        )
    )
    return _config


def save_json_log(data, file_name, conf):
    file_name = datetime.now().strftime("%Y%m%d_%H%M%S") + "_" + file_name
    full_path = os.path.join(conf["logging_json"], file_name)
    with open(full_path, "w", encoding="utf8") as f_out:
        f_out.write(data)


def adjust_response_header(response):
    response.content_type = "application/json; charset=UTF-8"
    response.headers["Server"] = "InfoWS3"
    return response


def adjust_file_response_header(file_path):
    file_header_path = file_path + ".txt"
    response = static_file(file_path, root=os.path.dirname(file_path), download=False)
    response.headers["Server"] = "InfoWS3"
    response.headers["X-Extension"] = file_path.split(".")[-1]
    response.headers["Content-Transfer-Encoding"] = "binary"
    if os.path.exists(file_header_path):
        with open(file_header_path, 'r') as f_in:
            lines = [line.strip() for line in f_in.readlines() if line]
            for line in lines:
                key, value = line.split(":", 1)
                response.headers[key.strip()] = value.strip()
    return response


def validate_request(data, conf, cache):
    result = {"error": ""}

    if data["PdaError"] and not data["json"]:
        return result

    # Check HTTP param APIVersion
    if conf["restricted_mode_validations"].get("apiversion_check", "N") == "D" and not data["APIVersion"]:
        result["error"] = "APIVersion do not exists."
        return result

    # Check required params from JSON
    try:
        json_data = json.loads(data["json"])
    except json.decoder.JSONDecodeError:
        result["error"] = f"Empty request to API endpoint."
        return result

    for check in conf["restricted_mode_validations"].get("fields_exists", []):
        if not json_data.get(check, None):
            result["error"] = f"{check} do not exists."
            return result

    # Check no repeat params from JSON
    for check in conf["restricted_mode_validations"].get("fields_no_repeat", []):
        old_cache_ip = cache.get(data["remote_ip"], None)
        if old_cache_ip:
            old_cache_ip = old_cache_ip.get(check, None)

        if old_cache_ip:
            cache[data["remote_ip"]][check] = json_data.get(check, None)
        else:
            cache[data["remote_ip"]] = {
                check: json_data.get(check, None)
            }
        
        if old_cache_ip and old_cache_ip == json_data.get(check, None):
            result["error"] = f"{check} is same as in last request."
            return result

    return result


def get_log_data(today, app):
    v0_index = [0, 1, 2, 4, 5, 6, 9, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 23]
    v1_index = [0, 5, 6, 8, 9, 10, 13, 15, 16, 17, 18, 19, 20, 22, 23, 24, 25, 26, 27, 28, 29]
    log_data = []

    file_name = f"log_{today.replace('-', '')}.log"
    full_path = os.path.join(app.config["CONF"]["api_log_path"], file_name)
    if os.path.exists(full_path):
        with open(full_path, 'r') as f_in:
            tmp_log_data = [x.split("\t") for x in f_in.readlines() if x.strip()]
            for idx, row in enumerate(tmp_log_data):
                log_data.append([x for i, x in enumerate(row) if i in v1_index])

    # append v0 log file
    file_name = f"log_{today.replace('-', '')}-v0.log"
    full_path = os.path.join(app.config["CONF"]["api_log_path"], file_name)
    if os.path.exists(full_path):
        with open(full_path, 'r') as f_in:
            tmp_log_data = [x.split("\t") for x in f_in.readlines() if x.strip()]
            for idx, row in enumerate(tmp_log_data):
                log_data.append(
                    [x for i, x in enumerate(row) if i in v0_index] + ["/", "/"]
                )

    # check and validate fields
    log_data = [x for x in log_data if not len(x) < 18]
    for row in log_data:
        row[-1] = row[-1].strip()
        row[0] = row[0].split("T")
        if not row[-1]:
            row.append(-1)
        elif not row[7]:
            row.append(-2)
        elif not row[14]:
            row.append(-3)
        elif row[5].strip() != "ok":
            row.append(-4)
        elif row[4].strip() != "ok":
            row.append(-6)
        elif row[18].strip() != "Nema greske" and row[18].strip() != "":
            row.append(-5)
        else:
            row.append(1)

    log_data.sort(key=lambda x: x[0][0] + x[0][1], reverse=True)
    # check and validate repeats of req_id
    for idx, row in enumerate(log_data):
        if idx > 0 and log_data[idx][7] == log_data[idx-1][7]:
            log_data[idx-1][-1] = -2

    return log_data


def get_list_of_process():
    """
    Get list of running process sorted by Memory Usage
    """
    proc_objects = []
    for proc in psutil.process_iter():
        try:
            p_info = proc.as_dict(attrs=["pid", "name", "cpu_percent"])
            p_info["vms"] = proc.memory_info().vms / (1024 * 1024)
            proc_objects.append(p_info);
        except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
            pass
    return sorted(filter(lambda x: "python" in x["name"], proc_objects), key=lambda proc_obj: proc_obj["vms"], reverse=True)
