import os, io, sys, platform, shutil, time, json, datetime, psutil
import re, docker, requests
from api.utils import shell_execute
from dotenv import load_dotenv, find_dotenv
import dotenv
from pathlib import Path
from api.utils.common_log import myLogger
from api.utils import shell_execute, const
from api.exception.command_exception import CommandException
from api.service import manage


# 已经是running的app怎么知道它已经能够访问，如页面能进入，如mysql能被客户端连接
def if_app_access(app_name):
    return True


def if_app_exits(app_name):
    cmd = "docker compose ls -a"
    output = shell_execute.execute_command_output_all(cmd)
    if int(output["code"]) == 0:
        pattern = app_name + '$'
        info_list = output['result'].split()
        is_exist = False
        for info in info_list:
            if re.match(pattern, info) != None:
                is_exist = True
                break
        return is_exist
    else:
        return True


def if_app_running(app_name):
    cmd = "docker compose ls -a"
    output = shell_execute.execute_command_output_all(cmd)
    if int(output["code"]) == 0:
        app_list = output['result'].split("\n")
        pattern = app_name + '\s*'
        if_running = False
        for app in app_list:
            if re.match(pattern, app) != None and re.match('running', app) != None:
                if_running = True
                break
        return if_running
    else:
        return False


def check_appid_exist(app_id):
    myLogger.info_logger("Checking check_appid_exist ...")
    appList = manage.get_my_app()
    find = False
    for app in appList:
        if app_id == app.app_id:
            find = True
            break
    myLogger.info_logger("Check complete.")
    return find


def check_appid_include_rq(app_id):
    message = ""
    code = None
    if app_id == None:
        code = const.ERROR_CLIENT_PARAM_BLANK
        message = "AppID is null"
    elif re.match('^[a-z0-9]+_[a-z0-9]+$', app_id) == None:
        code = const.ERROR_CLIENT_PARAM_Format
        message = "APP name can only be composed of numbers and lowercase letters"
    elif not check_appid_exist(app_id):
        code = const.ERROR_CLIENT_PARAM_NOTEXIST
        message = "AppID is not exist"
    return code, message


def check_app_id(app_id):
    message = ""
    code = None
    if app_id == None:
        code = const.ERROR_CLIENT_PARAM_BLANK
        message = "AppID is null"
    elif re.match('^[a-z0-9]+_[a-z0-9]+$', app_id) == None:
        code = const.ERROR_CLIENT_PARAM_Format
        message = "APP name can only be composed of numbers and lowercase letters"
    myLogger.info_logger(code)
    return code, message


def check_vm_resource(app_name):
    myLogger.info_logger("Checking virtual memory resource ...")
    var_path = "/data/library/apps/" + app_name + "/variables.json"
    requirements_var = read_var(var_path, 'requirements')
    need_cpu_count = int(requirements_var['cpu'])
    cpu_count = int(shell_execute.execute_command_output_all("cat /proc/cpuinfo | grep \'core id\'| wc -l")["result"])
    if cpu_count < need_cpu_count:
        myLogger.info_logger("Check complete: The number of CPU cores is insufficient!")
        return False
    need_mem_total = int(requirements_var['memory'])
    mem_free = float(psutil.virtual_memory().available) / 1024 / 1024 / 1024
    if mem_free < need_mem_total * 1.2:
        myLogger.info_logger("Check complete: The total amount of memory is insufficient!")
        return False
    need_disk = int(requirements_var['disk'])
    disk_free = float(psutil.disk_usage('/').free) / 1024 / 1024 / 1024
    if round(disk_free) < need_disk + 2:
        myLogger.info_logger("Check complete: There are not enough disks left!")
        return False
    myLogger.info_logger("Check complete.")
    return True


def check_app_websoft9(app_name):
    # websoft9's support applist
    myLogger.info_logger("Checking dir...")
    path = "/data/library/apps/" + app_name
    is_exists = check_directory(path)
    return is_exists


def check_directory(path):
    try:
        shell_execute.execute_command_output_all("ls " + path)
        return True
    except CommandException as ce:
        return False

def check_app_compose(app_name, customer_name):
    myLogger.info_logger("Checking port...")
    library_path = "/data/library/apps/" + app_name
    install_path = "/data/apps/" + customer_name
    port_dic = read_env(library_path + '/.env', "APP_.*_PORT")
    # 1.判断/data/apps/app_name/.env中的port是否占用，没有被占用，方法结束（get_start_port方法）
    cmd1 = "docker container inspect $(docker ps -aq) | grep HostPort | awk \'{print $2}\' | sort -u"
    cmd2 = "netstat -tunlp | grep \"LISTEN\" | awk '{print $4}' | awk -F \":\" '{print $NF}' | sort -u"
    cmd3 = "grep -r \"APP_.*_PORT=\" /data/apps/*/.env | awk -F \"=\" '{print $2}' | sort -u"
    s1 = shell_execute.execute_command_output_all(cmd1)['result'].replace('\"', '')
    s2 = shell_execute.execute_command_output_all(cmd2)['result']
    try:
        s3 = ''
        s3 = shell_execute.execute_command_output_all(cmd3)['result']
    except:
        pass
    s = s1 + '\n' + s2 + '\n' + s3

    shell_execute.execute_command_output_all("cp -r " + library_path + " " + install_path)
    for port_name in port_dic:
        port_value = get_start_port(s, port_dic[port_name])
        modify_env(install_path + '/.env', port_name, port_value)
    myLogger.info_logger("Port check complete")
    return


def check_app_url(customer_app_name):
    myLogger.info_logger("Checking app url...")

    # 如果app的.env文件中含有HTTP_URL项目,需要如此设置 HTTP_URL=ip:port
    env_path = "/data/apps/" + customer_app_name + "/.env"
    if read_env(env_path, "HTTP_URL") != {}:
        ip = "localhost"
        try:
            ip_result = shell_execute.execute_command_output_all("cat /data/apps/stackhub/docker/w9appmanage/public_ip")
            ip = ip_result["result"].rstrip('\n')
        except Exception:
            ip = "127.0.0.1"
        http_port = list(read_env(path, "APP_HTTP_PORT").values())[0]
        url = ip + ":" + http_port
        modify_env(path, "HTTP_URL", url)

    myLogger.info_logger("App url check complete")
    return


def read_env(path, key):
    myLogger.info_logger("Read " + path)
    output = shell_execute.execute_command_output_all("cat " + path)
    code = output["code"]
    env_dic = {}
    if int(code) == 0:
        ret = output["result"]
        env_list = ret.split("\n")
        for env in env_list:
            if re.match(key, env) != None:
                env_dic[env.split("=")[0]] = env.split("=")[1]
    myLogger.info_logger("Read " + path + ": " + str(env_dic))
    return env_dic


def modify_env(path, env_name, value):
    myLogger.info_logger("Modify " + path + "...")
    output = shell_execute.execute_command_output_all("sed -n \'/^" + env_name + "/=\' " + path)
    if int(output["code"]) == 0 and output["result"] != "":
        line_num = output["result"].split("\n")[0]
        s = env_name + "=" + value
        output = shell_execute.execute_command_output_all("sed -i \'" + line_num + "c " + s + "\' " + path)
        if int(output["code"]) == 0:
            myLogger.info_logger("Modify " + path + ": Change " + env_name + " to " + value)


def read_var(var_path, var_name):
    value = ""
    myLogger.info_logger("Read " + var_path)
    output = shell_execute.execute_command_output_all("cat " + var_path)
    if int(output["code"]) == 0:
        var = json.loads(output["result"])
        try:
            value = var[var_name]
        except KeyError:
            myLogger.warning_logger("Read " + var_path + ": No key " + var_name)
    else:
        myLogger.warning_logger(var_path + " not found")
    return value


def get_start_port(s, port):
    use_port = port
    while True:
        if s.find(use_port) == -1:
            break
        else:
            use_port = str(int(use_port) + 1)

    return use_port
