import os
import subprocess
import psutil
import time
import webbrowser
import json
import glob
import requests
import argparse
import io
import re


current_dir = os.path.dirname(os.path.abspath(__file__))
weshop_native_dir = os.path.dirname(current_dir)
resources_dir = os.path.join(weshop_native_dir, "resources")
package_dir = os.path.join(weshop_native_dir, "packages")
bin_dir = os.path.join(weshop_native_dir, "bin")
log_dir = os.path.join(weshop_native_dir, "logs")
err_dir = os.path.join(resources_dir, "err")
python_exe = os.path.join(bin_dir, "python","python.exe")
userData_dir = os.path.join(weshop_native_dir, "userData")
gpu_pid_file = os.path.join(weshop_native_dir, "bin", "gpu_service.txt")
electron_status_file = os.path.join(weshop_native_dir, "logs", "status.log")
models_dir = os.path.join(weshop_native_dir, "models")

parser = argparse.ArgumentParser()
parser.add_argument('--portnum', type=int, help='arguments', default=30000)
parser.add_argument('--username', type=str, help='arguments', default="")
parser.add_argument('--pwd', type=str, help='arguments', default="")
parser.add_argument('--lang', type=str, help='arguments', default="zh-CN")

args = parser.parse_args()

# port_num = parsed_query.get('portnum', [None])[0]
port_num = args.portnum
username = args.username
pwd = args.pwd
lang = args.lang
isEn = lang == 'en-US'


class ConfigReader():
    def __init__(self) -> None:
        self.config_path = os.path.join(bin_dir, "config.json")
        with open(self.config_path, "r") as config_file:
            config_data = json.load(config_file)
        self.log_mode = config_data["log_mode"]
        self.config_data = config_data

    def write(self, key, value):
        with open(self.config_path, "r") as config_file:
            config_data = json.load(config_file)
        config_data[key] = value
        with open(self.config_path, "w") as config_file:
            json.dump(config_data, config_file)


configReader = ConfigReader()
            
    

def kill_processes_on_ports(port_numbers):
    for conn in psutil.net_connections():
        if conn.status == 'LISTEN' and conn.laddr.port in port_numbers:
            process = psutil.Process(conn.pid)
            process.terminate()

def clear_env():
    port_num_list = [int(port_num), 8080, 13020, 60020, 60030, 60040, 30001, 30002, 30003]
    kill_processes_on_ports(port_num_list)

def record_system_info():
    import platform
    import psutil

    # Get CPU make
    cpu_make = platform.processor()

    # Get RAM size
    ram_info = {}
    mem = psutil.virtual_memory()
    ram_info['size'] = mem.total

    # Get RAM usage
    ram_usage = mem.used

    # Get OS
    os_info = {}
    os_info['name'] = platform.system()
    os_info['version'] = platform.version()
    output = subprocess.check_output(['nvidia-smi', '--query-gpu=utilization.gpu,name,memory.used,memory.total', '--format=csv,nounits,noheader'])
    lines = output.decode().strip().split('\n')
    gpu_info = lines[0].split(',')

    gpu_make = gpu_info[1]  # Index 1 corresponds to GPU make
    gpu_utilization = float(gpu_info[0])  # Index 0 corresponds to GPU utilization
    vram_used = int(gpu_info[2])  # Index 2 corresponds to VRAM used
    vram_total = int(gpu_info[3])  # Index 3 corresponds to total VRAM

    gpu_info = {
        'GpuMake': gpu_make.strip(),
        'vramUsage' : f"{ round(vram_used/ 1024, 2)} / {round(vram_total/ 1024, 2)} GB",
        'GpuUtilization': f"{round(gpu_utilization, 1)} %"
    } 

    with open(os.path.join(log_dir, "systemInfo.log"), "w") as log:
        print(f"CPU Make: {cpu_make}", file=log, flush=True)
        print(f"RAM Size: {ram_info['size']}", file=log, flush=True)
        print(f"RAM Usage: {ram_usage}", file=log, flush=True)
        print(f"OS: {os_info['name']}", file=log, flush=True)
        print(f"OS Version:{os_info['version']}", file=log, flush=True)
        print(f"GPU Model: {gpu_info['GpuMake']}", file=log, flush=True)
        print(f"GPU Vram size: {gpu_info['vramUsage']}", file=log, flush=True)



def run_mongo():
    mongod_exe = os.path.join(package_dir, "MongoDB", "Server", "7.0","bin","mongod.exe")
    db_path = os.path.join(userData_dir,"User", "mongo_replica")
    keyfile = os.path.join(package_dir, "MongoDB", "Server", "7.0","bin", "keyfile")
    with open(os.path.join(log_dir, "mongo.log"), configReader.log_mode) as f:
        mongo_process = subprocess.Popen([mongod_exe, "--replSet", "rs0", "--port", "27017", "--bind_ip", "127.0.0.1", "--dbpath", db_path, "--auth", "--keyFile", keyfile], stdout=f, stderr=f )
    return mongo_process.pid

def run_nginx():

    nginx_dir = os.path.join(package_dir, "nginx")
    config_file_path = os.path.join(nginx_dir, "conf", "weshop-native.conf")
    auth_file_path = os.path.join(nginx_dir, "conf",".users.htpasswd")
    auth_file_path = auth_file_path.replace("\\","/")
    flag = False if pwd.strip() == "" or pwd == "undefined" else True
    htpasswd = f"{username}:{pwd}"
    with open(auth_file_path, "w") as f:
        f.write(htpasswd)
    template_nginx_config_lines = [
        'client_max_body_size 20M;\n',
        'keepalive_timeout 300s;\n', 
        'server {\n', 
        f'    listen {port_num};\n', 
        '    server_name localhost;\n', 
        '    charset utf8;\n', 
        '\n    location /api {\n', 
        '        proxy_pass http://127.0.0.1:8080;\n', 
        '        proxy_read_timeout 300s;\n', 
        "    \n        if ($request_method = 'OPTIONS') {\n", 
        "            add_header 'Access-Control-Allow-Origin' '*';\n", 
        "            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';\n", 
        '            # \n', 
        "            # Custom headers and headers various browsers *should* be OK with but aren't\n", 
        '            # \n', 
        "            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Weshop-Language,Referer' always;\n", 
        '            # \n', 
        '# Tell client that this pre-flight info is valid for 20 days\n', 
        '            # \n', 
        "            add_header 'Access-Control-Max-Age' 1728000;\n", 
        '            add_header \'Content-Type\' \'text/plain; charset=utf-8\';\n', 
        "            add_header 'Content-Length' 0;\n", 
        '            return 204;\n', 
        '        }\n\n', 
        "    \n        if ($request_method = 'POST') {\n", 
        "            add_header 'Access-Control-Allow-Origin' '*' always;\n", 
        "            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;\n", 
        "            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Weshop-Language,Referer' always;\n", 
        "            add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;\n", 
        '        }\n\n', 
        "    \n        if ($request_method = 'GET') {\n", 
        "            add_header 'Access-Control-Allow-Origin' '*' always;\n", 
        "            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;\n", 
        "            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Weshop-Language,Referer' always;\n", 
        "            add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;\n", 
        '        }\n', 
        '    }\n\n', 
        '\n    location /nativestorage {\n', 
        '        rewrite ^/nativestorage/(.*) /$1 break;\n', 
        '        proxy_read_timeout 300s;\n', 
        '        proxy_pass http://127.0.0.1:60020;\n', 
        "        add_header 'Access-Control-Allow-Origin' '*' always;\n", 
        "        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;\n", 
        "        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Weshop-Language,Referer' always;\n", 
        "        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;\n", 
        '    }\n\n', 
        '\n    location /nativefile {\n', 
        '        rewrite ^/nativefile/(.*) /$1 break;\n', 
        '        proxy_read_timeout 300s;\n', 
        '        proxy_pass http://127.0.0.1:60021;\n', 
        "        add_header 'Access-Control-Allow-Origin' '*' always;\n", 
        "        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;\n", 
        "        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Weshop-Language,Referer' always;\n", 
        "        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;\n", 
        '    }\n\n', 
        '\n    location / {\n', 
        '        proxy_read_timeout 300s;\n', 
        '        proxy_pass http://127.0.0.1:13020;\n', 
        '        auth_basic "Restricted Access";\n' if flag else '        #auth_basic "Restricted Access";\n', 
        f'        auth_basic_user_file {auth_file_path};\n'if flag else f'        #auth_basic_user_file {auth_file_path};\n', 
        '    }\n', 
        '}\n\n', 
        '# sam segment\n', 
        'server {\n', 
        '    listen 30001;\n', 
        '    server_name localhost;\n', 
        '    charset utf8;\n', 
        '\n    location = /api/imgseg-base64 {\n', 
        '        proxy_read_timeout 300s;\n', 
        '        proxy_pass http://127.0.0.1:60030;\n', 
        '    }\n\n', 
        '\n    location = /download {\n', 
        '        proxy_read_timeout 300s;\n', 
        '        proxy_pass http://127.0.0.1:60030;\n', 
        '    }\n\n', 
        '\n    location = /api/imgto4k {\n', 
        '        proxy_read_timeout 300s;\n', 
        '        proxy_pass http://127.0.0.1:60030;\n', 
        '    }\n\n', 
        '\n    location = /api/facedet {\n', 
        '        proxy_read_timeout 300s;\n', 
        '        proxy_pass http://127.0.0.1:60030;\n', 
        '    }\n', 
        '}\n\n', 
        '# body segment\n', 
        'server {\n', 
        '    listen 30002;\n', 
        '    server_name localhost;\n', 
        '    charset utf8;\n', 
        '\n    location = /api/imgseg-base64 {\n', 
        '        rewrite ^ /api/imgseg-base64-sos break;\n', 
        '        proxy_read_timeout 300s;\n', 
        '        proxy_pass http://127.0.0.1:60030;\n', 
        '    }\n', 
        '}\n'
    ]
    with open(config_file_path, "w") as f:
        f.writelines(template_nginx_config_lines)

    nginx_dir = os.path.join(package_dir, "nginx")
    os.chdir(nginx_dir)
    nginx_exe = os.path.join(nginx_dir,"nginx.exe")
    # config = NginxConfig()
    # config.load(os.path.join(nginx_dir, "conf", "weshop-native.conf"))
    with open(os.path.join(log_dir,"nginx.log"), configReader.log_mode) as f:
        nginx_process = subprocess.Popen([nginx_exe], stdout=f, stderr=f)
    os.chdir(current_dir)
    return nginx_process.pid

def setup_webui_env(webui_dir,console_log_file):
    git_path = os.path.join(webui_dir, "git", "cmd", "git.exe")
    hf_cache_home = os.path.join(webui_dir, ".cache", "huggingface")

    os.environ["GIT_PYTHON_GIT_EXECUTABLE"] = git_path
    os.environ["PATH"] = os.environ.get("PATH", "") + os.pathsep + git_path
    os.environ["HF_HOME"] = hf_cache_home
    os.environ["WESHOP_CONSOLE_LOG_FILE"] = console_log_file
    os.environ["HF_HUB_OFFLINE"] = "1"


def run_monitor():
    os.chdir(current_dir)
    with open(os.path.join(log_dir, "monitor.log"), configReader.log_mode) as log:
        p = subprocess.Popen([python_exe, "file_monitor.py"], stdout=log, stderr=log)
    return p.pid



class MultiFileWriter(io.TextIOWrapper):
    def __init__(self, file_objects, **kwargs):
        self.file_objects = file_objects
        super().__init__(self.file_objects[0], **kwargs)
        
    def write(self, *args, **kwargs):
        for file_obj in self.file_objects:
            file_obj.write(*args, **kwargs)


def run_webui(weshop_console_log, loaded_model_path):
    webui_dir = os.path.join(package_dir, "stable-diffusion-webui")
    setup_webui_env(webui_dir=webui_dir,console_log_file=weshop_console_log)
    launch_script = os.path.join(webui_dir, "launch.py")
    sd_models_dir = os.path.join(weshop_native_dir, "models", "Stable-diffusion")
    vae_dir = os.path.join(weshop_native_dir, "models", "VAE")
    lora_dir = os.path.join(weshop_native_dir,"models", "Lora")
    embedding_dir = os.path.join(weshop_native_dir, "models", "Embedding")
    clip_dir = os.path.join(webui_dir,".cache", "clip")
    os.environ["PROJECT_DIR"] = weshop_native_dir
    os.environ["APP_PORT_NUM"] = str(port_num)
    command = [
    python_exe,
    launch_script,
    # f'--ckpt={loaded_model_path}',
    "--theme",
    "dark",
    "--xformers",
    "--no-half-vae",
    # "--nowebui",
    "--api",
    "--skip-prepare-environment",
    "--port=60040",
    "--server-name=127.0.0.1",
    "--skip-version-check",
    "--no-download-sd-model",
    "--ad-no-huggingface",
    "--disable-nan-check",
    "--do-not-download-clip",
    "--disable-console-progressbars",
    "--controlnet-preprocessor-cache-size=0",
    "--no-hashing",
    f"--clip-models-path={clip_dir}",
    f"--vae-dir={vae_dir}",
    f"--ckpt-dir={sd_models_dir}",
    f"--lora-dir={lora_dir}",
    f"--embeddings-dir={embedding_dir}"
    ]

    with open(os.path.join(log_dir, "webui.log"), configReader.log_mode) as f, open(start_log, "a", encoding='utf-8') as start_log_file :
        multi_writer = MultiFileWriter([f, start_log_file])
        webui_process = subprocess.Popen(command, stdout=start_log_file, stderr=start_log_file)
    return webui_process.pid

# def run_local_file_server():
#     lfs_dir = os.path.join(package_dir, "weshop_local_file_server_service")
#     lfs_exe = os.path.join(lfs_dir, "http-file-server.exe")
#     lfs_data_dir = os.path.join(userData_dir, "Data")
#     command = [lfs_exe, "-uploads", "-addr", "127.0.0.1:60020", "-", f"/={lfs_data_dir}"]
#     with open(os.path.join(log_dir, "lfs.log"), configReader.log_mode) as f:
#         lfs_process = subprocess.Popen(command, stderr=f, stdout= f)
#     return lfs_process.pid

def run_local_file_server():
    lfs_dir = os.path.join(package_dir, "weshop_local_file_server_service")
    lfs_exe = os.path.join(lfs_dir, "http-file-server.exe")
    imageserver_exe = os.path.join(lfs_dir, "imageserver.exe")
    lfs_data_dir = os.path.join(userData_dir, "Data")
    image_data_dir = os.path.join(userData_dir, "Data")
    command = [lfs_exe, "-uploads", "-addr", "127.0.0.1:60020", "-", f"/={lfs_data_dir}"]
    image_command = [imageserver_exe, "-http", "127.0.0.1:60021"]
    with open(os.path.join(log_dir, "lfs.log"), configReader.log_mode) as f:
        lfs_process = subprocess.Popen(command, stderr=f, stdout= f)
    os.environ["dataDir"] = image_data_dir
    with open(os.path.join(log_dir, "image_lfs.log"), configReader.log_mode) as f:
        image_process = subprocess.Popen(image_command, stderr=f, stdout= f)
    return lfs_process.pid, image_process.pid


def is_port_in_use(port):
    try:
        # Create a socket object
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            # Try to bind the socket to the specified port
            s.bind(("localhost", port))
            return False  # Port is available
    except OSError:
        return True  # Port is already in use

def run_backend():
    # Set the environment variables
    os.environ["CONFIG_FILE"] = "application-va-native" if isEn else "application-cn-native" 
    os.environ["WEBUI_MODELS"] = os.path.join(package_dir, "stable-diffusion-webui", "models")
    os.environ["APP_PORT_NUM"] = str(port_num)
    os.environ["APP_USERNAME"] = str(username)
    os.environ["APP_PWD"] = str(pwd) 

    backend_dir = os.path.join(package_dir, "weshop-native-backend")
    os.chdir(backend_dir)
    with open(os.path.join(log_dir, "backend.log"), configReader.log_mode) as f:
        backend_process = subprocess.Popen(r".\weshop-web-server", stdout=f, stderr=f)
    with open(os.path.join(log_dir, "scheduler.log"), configReader.log_mode) as f:
        scheduler_process = subprocess.Popen(r".\weshop-scheduler.exe", stdout=f, stderr=f)
    return backend_process.pid, scheduler_process.pid

def run_frontend():
    frontend_dir = os.path.join(package_dir, "weshop-native-frontend")
    node_folder_name = "node-v18.18.0-win-x64"
    node_folder = os.path.join(frontend_dir, node_folder_name)
    
    # Update the PATH environment variable
    os.environ["PATH"] = os.environ.get("PATH", "") + os.pathsep + node_folder
    weshop_ai_model_feature_local_dir = os.path.join(frontend_dir, "weshop-ai-model")
    os.chdir(weshop_ai_model_feature_local_dir)

    with open(os.path.join(log_dir, "frontend.log"), configReader.log_mode) as f:
        frontend_process = subprocess.Popen(["npm", "run", "start:localEN" if isEn else "start:local", "--", "-p", "13020"], stdout=f, stderr=f, shell=True)
    
    return frontend_process.pid


def run_broker(loaded_model, vae):
    broker_dir = os.path.join(package_dir, "weshop-sd-broker")
    os.chdir(broker_dir)
    sidecar_command = f'.\sd-sidecar.exe --checkpoint="{loaded_model}" --vae="{vae}" --webui=http://127.0.0.1:60040 --broker=127.0.0.1:60041'
    broker_command = f".\sd-broker.exe --port=60041 --verifyToken=false"
    with open(os.path.join(log_dir, "sidecar.log"), configReader.log_mode) as f:
        sidecar_process = subprocess.Popen(sidecar_command, stdout=f, stderr=f)
    with open(os.path.join(log_dir, "broker.log"), configReader.log_mode) as f:
        broker_process = subprocess.Popen(broker_command, stdout=f, stderr=f)
    return sidecar_process.pid, broker_process.pid

    

def check_status(pid_list, status_log_dir):
    while True:
        time.sleep(1)
        initial_flag = True
        for pid in pid_list[:-1]:
            if psutil.pid_exists(pid):
                initial_flag = False
        with open(status_log_dir, "r") as log:
            first_line = log.readline()
        previous_status = False
        if first_line == "weshop native status code: 2001":
            previous_status = False
        elif first_line == "weshop native status code: 1001":
            previous_status = True
        else:
            previous_status = False
        if initial_flag == previous_status:
            with open(status_log_dir, "w") as log:
                log_content = "weshop native status code: 1001" if initial_flag else "weshop native status code: 2001"
                log.write(log_content)


def check_mongo():
    from pymongo import MongoClient
    start_time  = time.time()
    end_time = start_time + 60
    result = 0
    connection_string = "mongodb://ping:ping@localhost:27017/test?replicaSet=rs0"

    while time.time() < end_time:
        try:
            client = MongoClient(connection_string)
            # # Access a collection or perform any operation to test the connection
            server_info = client.server_info()
            if server_info:
                # result = 1
                client.close()
                # break
                result = 1
                break
            else:
                client.close()
            print("try to connect to mongo")
        except Exception as e:
            print(e)


def create_or_save_port_num():
    port_num_json_path = os.path.join(bin_dir, "port_num.json")
    with open(port_num_json_path, "w") as json_file:
        json.dump({"port_num": port_num}, json_file)
    os.environ["WESHOP_PORT_NUM"] = str(port_num)


def check_webui():
    website = "http://localhost:60040/sdapi/v1/ping"

    start_time = time.time()
    end_time = start_time + 90  # Set the time limit to 1.5 minutes (90 seconds)

    while time.time() < end_time:
        try:
            response = requests.get(website)
            if response.status_code == 200 and response.text.strip() == "1":
                print("Ping webui success")
                return 1  # Return 1 if success
        except requests.exceptions.RequestException as e:
            pass  # Ignore exception and continue looping

    print("Time limit reached. Ping webui failed")
    return 0  # Return 0 if failed

def check_local_file_server():
    url = "http://127.0.0.1:60020"


    start_time = time.time()
    end_time = start_time + 30  # Set the time limit to 1.5 minutes (90 seconds)

    while time.time() < end_time:
        try:
            response = requests.get(url)
            if response.status_code == 200:
                print("Ping local file server success.")
                return 1  # Return 1 if success
        except requests.exceptions.RequestException as e:
            pass  # Ignore exception and continue looping

    print("Time limit reached. Ping local file server failed")
    return 0  # Return 0 if failed

def check_gpu_service():
    website = "http://localhost:60030/api/ping"

    print("Start pinging GPU service")

    start_time = time.time()
    end_time = start_time + 60  # Set the time limit to 1 minute (60 seconds)

    while time.time() < end_time:
        try:
            response = requests.get(website)
            if response.status_code == 200 and response.text.strip() == "1":
                print("Ping GPU service success")
                return 1  # Return 1 if success
        except requests.exceptions.RequestException as e:
            pass  # Ignore exception and continue looping

    print("Time limit reached. Ping GPU service failed")
    return 0  # Return 0 if failed

def run_gpu_service():
    gpu_service_dir = os.path.join(package_dir, "weshop-gpu-service")
    launch_script = os.path.join(gpu_service_dir, "app_server.py")
    os.chdir(gpu_service_dir)
    os.environ["PROJECT_DIR"] = weshop_native_dir
    os.environ["APP_PORT_NUM"] = str(port_num)
    with open(os.path.join(log_dir, "gpu_service.log"), configReader.log_mode) as f:
        gpu_service_process = subprocess.Popen([python_exe, launch_script], stdout=f, stderr=f)
    return gpu_service_process.pid

def terminate_process_by_pid(pid):
    try:
        process = psutil.Process(pid)
        process.terminate()
        print(f"Process with PID {pid} terminated successfully.")
    except psutil.NoSuchProcess:
        print(f"No process found with PID {pid}.")
    except psutil.AccessDenied:
        print(f"Access denied to terminate process with PID {pid}.")

def terminate_process_tree(parent_pid):
    parent = psutil.Process(parent_pid)
    children = parent.children(recursive=True)
    
    # Terminate child processes recursively
    for child in children:
        child.terminate()
        
    # Wait for termination of child processes
    psutil.wait_procs(children, timeout=5)
    
    # Terminate the parent process
    parent.terminate()

import socket
def is_port_occupied(port):
    # Create a socket object
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        
        try:
            # Try to connect to the specified port
            sock.connect(('localhost', port))
            # If the connection succeeds, the port is occupied
            return True
        except ConnectionRefusedError:
            # If the connection is refused, the port is available
            return False
        finally:
            sock.close()

def check_env():
    total_vram = psutil.virtual_memory().total / (1024 ** 3)  # Convert to GB
    port_list = [60020, 60030, 60040, 27017]


    if total_vram > 7.9:
        return 1
    else:
        return 0

def terminate_processes(pids):
    for pid in pids:
        try:
            parent_process = psutil.Process(pid)
            child_processes = parent_process.children(recursive=True)
            
            # Terminate child processes
            for child in child_processes:
                child.terminate()
                print(f"Terminated process{pid}'s subprocess {child.pid}")
                
            # Wait for child processes to terminate
            psutil.wait_procs(child_processes)

            # Terminate the parent process
            parent_process.terminate()
            
            print(f"Terminated process {pid} and its child processes.")
        
        except psutil.NoSuchProcess:
            print(f"No such process with PID {pid}.")

def check_web_server():
    url = "http://127.0.0.1:8080/api/user"

    print("Start pinging web server")

    start_time = time.time()
    end_time = start_time + 60  # Set the time limit to 1 minute (60 seconds)

    while time.time() < end_time:
        try:
            response = requests.get(url)
            if response.status_code == 200:
                print("Ping web server success.")
                return 1  # Return 1 if success
        except requests.exceptions.RequestException as e:
            pass  # Ignore exception and continue looping

    print("Time limit reached. Ping web server failed")
    return 0  # Return 0 if failed




def check_frontend():
    url = f"http://127.0.0.1:{port_num}"

    print(f"Start pinging frontend")

    start_time = time.time()
    end_time = start_time + 60  # Set the time limit to 1 minute (60 seconds)
    times = 0

    while time.time() < end_time:
        try:
            response = requests.get(url)
            http_code = response.status_code
            times = times + 1

            if http_code == 200 or http_code == 304 or http_code == 401:
                print("Ping frontend success")
                return 1  # Return 1 if success
            else:
                pass
        except requests.exceptions.RequestException as e:
            pass  # Ignore exception and continue looping

    print("Time limit reached. Ping frontend failed")
    return 0  # Return 0 if failed


class Progress_bar_json_writer():
    def __init__(self) -> None:
        self.path = os.path.join(log_dir,"progress_bar.json")
    def write(self, status, text, progress) -> None:
        with open(self.path, "w") as f:
            data = {"progress": progress, "status": status, "text": text}
            json.dump(data, f)

if __name__ == '__main__':
    pid_json = os.path.join(bin_dir,"pid.json")
    file_pattern = os.path.join(log_dir, "*.log")
    progress_bar_json_writer = Progress_bar_json_writer()
    progress_bar_json_writer.write(progress=0,status="pending",text="开始启动")
    start_log = os.path.join(log_dir, "start.log")
    # start_backup_log=os.path.join(log_dir, f"start_backup_{time.time()}.log")
    # os.rename(start_log, start_backup_log)
    with open(start_log, "w",encoding="utf-8",buffering=1) as log:
        log.truncate(0)
    start_backup_log = os.path.join(log_dir, f"start_backup_{time.time()}.log")
    with open(electron_status_file, "w") as electron_status:
        print("weshop native status code: 0001", file = electron_status, flush= True)

    # with open(start_log, "r") as start_log_file:
    #     log_content = start_log_file.read()
    # print(log_content, file=start_backup_log, flush=True)

    log_files = glob.glob(file_pattern)
    create_or_save_port_num()
    isPortOccupied = is_port_occupied(port_num)
    if isPortOccupied:
        logStr = f"The port {port_num} is already occupied" if isEn else f"{port_num}端口已被占用"
        with open(start_log, "a",encoding="utf-8",buffering=1) as log:
            print(logStr, file=log, flush=True)
        progress_bar_json_writer.write(progress=None,status="failed",text=logStr)

        with open(electron_status_file, "w") as electron_status:
            print("weshop native status code: 2001", file = electron_status, flush= True)

        os._exit(0)
    pathContainsChinese = bool(re.compile(r'[\u4e00-\u9fff]').search(weshop_native_dir))
    if pathContainsChinese:
        logStr = f"The installation path cannot contain Chinese characters" if isEn else f"安装路径不可以含有中文"
        with open(start_log, "a",encoding="utf-8",buffering=1) as log:
            print(logStr, file=log, flush=True)
        progress_bar_json_writer.write(progress=None,status="failed",text=logStr)

        with open(electron_status_file, "w") as electron_status:
            print("weshop native status code: 2001", file = electron_status, flush= True)

        os._exit(0)

    pid_list = []

    progress_bar_json_writer.write(progress=10,status="pending",text="Start environmental detection" if isEn else "开始进行环境检测")
    env_result = check_env()
    if env_result == 0:
        logStr = f"Graphics memory less than 8GB" if isEn else f"显存不足8GB"
        with open(start_log, "a",encoding="utf-8",buffering=1) as log:
            print(logStr, file=log, flush=True)
        progress_bar_json_writer.write(progress=None,status="failed",text=logStr)

        with open(electron_status_file, "w") as electron_status:
            print("weshop native status code: 2001", file = electron_status, flush= True)
        os._exit(0)
        
    record_system_info()
    progress_bar_json_writer.write(progress=20,status="pending",text="Start local file server" if isEn else "开启启动本地文件服务器")
    lfs_pid, image_pid = run_local_file_server()
    pid_list.append(lfs_pid)
    pid_list.append(image_pid)

    lfs_result = check_local_file_server()
    if lfs_result == 0:
        logStr = f"Start local file server failed" if isEn else f"本地文件服务器启动失败"
        with open(start_log, "a",encoding="utf-8",buffering=1) as log:
            print(logStr, file=log, flush=True)
        progress_bar_json_writer.write(progress=None,status="failed",text=logStr)

        with open(electron_status_file, "w") as electron_status:
            print("weshop native status code: 2001", file = electron_status, flush= True)

        terminate_processes(pid_list)
        with open(pid_json, "w", buffering=1) as file:
            json.dump({"pid":[]}, file)

        os._exit(0)
    with open(pid_json, "w", buffering=1) as file:
        json.dump({"pid": pid_list}, file)
    progress_bar_json_writer.write(progress=30,status="pending",text="Start database" if isEn else "开始启动数据库")


    monitor_p = run_monitor()
    pid_list.append(monitor_p)
    mongo_pid = run_mongo()
    pid_list.append(mongo_pid)

    mongo_result = check_mongo()
    if mongo_result == 0:
        logStr = f"Start database failed" if isEn else f"数据库启动失败"
        with open(start_log, "a",encoding="utf-8",buffering=1) as log:
            print(logStr, file=log, flush=True)
        progress_bar_json_writer.write(progress=None,status="failed",text=logStr)

        with open(electron_status_file, "w") as electron_status:
            print("weshop native status code: 2001", file = electron_status, flush= True)

        terminate_processes(pid_list)
        with open(pid_json, "w", buffering=1) as file:
            json.dump({"pid":[]}, file)
        os._exit(0)
    with open(pid_json, "w", buffering=1) as file:
        json.dump({"pid":pid_list}, file)
    nginx_pid = run_nginx()
    pid_list.append(nginx_pid)
    progress_bar_json_writer.write(progress=40,status="pending",text="Start webui" if isEn else "开始启动webui")

    sd_models_path = os.path.join(models_dir, "Stable-diffusion")
    default_sd_models_path = os.path.join(package_dir, "stable-diffusion-webui", "models", "Stable-diffusion")
    sd_models_path_files = [os.path.join(sd_models_path, path) for path in os.listdir(sd_models_path)] + [os.path.join(default_sd_models_path, path) for path in os.listdir(default_sd_models_path)]
    sd_model_files = [file for file in sd_models_path_files if os.path.splitext(file)[1] in [".ckpt", ".safetensors"]]


    if len(sd_model_files) < 1:
        logStr = f"Stable-diffusion model not exist. Please add the model to the \"models/Stable-diffusion\" directory" if isEn else f"未检测到Stable-diffusion模型，请添加模型至models/Stable-diffusion目录下"
        with open(start_log, "a",encoding="utf-8",buffering=1) as log:
            print(logStr, file=log, flush=True)
        progress_bar_json_writer.write(progress=None,status="failed",text=logStr)
        with open(electron_status_file, "w") as electron_status:
            print("weshop native status code: 2001", file = electron_status, flush= True)

        terminate_processes(pid_list)
        with open(pid_json, "w", buffering=1) as file:
            json.dump({"pid":[]}, file)

        os._exit(0)

    loaded_model_path = min(sd_model_files, key=lambda x:os.path.basename(x))
    loaded_model = os.path.basename(loaded_model_path)


    webui_pid = run_webui(start_log, loaded_model_path)
    print("webui pid is" + str(webui_pid))
    pid_list.append(webui_pid)
    webui_result = check_webui()
    if webui_result == 0:
        logStr = f"Start webui failed" if isEn else f"webui启动失败"
        with open(start_log, "a",encoding="utf-8",buffering=1) as log:
            print(logStr, file=log, flush=True)
        progress_bar_json_writer.write(progress=None,status="failed",text=logStr)
        with open(electron_status_file, "w") as electron_status:
            print("weshop native status code: 2001", file = electron_status, flush= True)

        terminate_processes(pid_list)
        with open(pid_json, "w", buffering=1) as file:
            json.dump({"pid":[]}, file)

        os._exit(0)
    with open(pid_json, "w", buffering=1) as file:
        json.dump({"pid":pid_list}, file)


    vae = "Automatic"

    response = requests.get("http://127.0.0.1:60040/sdapi/v1/options")
    if response.status_code == 200:
        json_data = response.json()
        vae = json_data["sd_vae"]
        loaded_model = json_data["sd_model_checkpoint"]
        # 去除后缀hash
        if ".safetensors" in loaded_model:
            loaded_model = loaded_model.split(".safetensors")[0] + ".safetensors"
        elif ".ckpt" in loaded_model:
            loaded_model = loaded_model.split(".ckpt")[0] + ".ckpt"
        loaded_model_path = os.path.join(sd_models_path, loaded_model)

    progress_bar_json_writer.write(progress=70,status="pending",text="Start GPU server" if isEn else "开始启动GPU服务")

    try: 
        with open(gpu_pid_file, "r") as gpu_pid_txt:
            gpu_pid = gpu_pid_txt.readline()
        with open(gpu_pid_file, "w") as gpu_pid_txt:
            gpu_pid_txt.truncate(0)
        pid_list.append(int(gpu_pid))
    except Exception as e:
        logStr = f"Start GPU server failed" if isEn else f"GPU服务启动失败"
        with open(start_log, "a",encoding="utf-8",buffering=1) as log:
            print(logStr, file=log, flush=True)
        progress_bar_json_writer.write(progress=None,status="failed",text=logStr)

        with open(electron_status_file, "w") as electron_status:
            print("weshop native status code: 2001", file = electron_status, flush= True)
        terminate_processes(pid_list)
        with open(pid_json, "w", buffering=1) as file:
            json.dump({"pid":[]}, file)
        os._exit(0)

    with open(pid_json, "w", buffering=1) as file:
        json.dump({"pid":pid_list}, file)

    progress_bar_json_writer.write(progress=80,status="pending",text="Start backend server" if isEn else "开始启动后端")

    backend_pid, scheduler_pid = run_backend()
    pid_list.append(backend_pid)
    pid_list.append(scheduler_pid)
    backend_result = check_web_server()
    if backend_result == 0:
        terminate_processes(pid_list)
        with open(pid_json, "w", buffering=1) as file:
            json.dump({"pid":[]}, file)
        logStr = f"Start backend failed" if isEn else f"后端启动失败"
        with open(start_log, "a",encoding="utf-8",buffering=1) as log:
            print(logStr, file=log, flush=True)
        progress_bar_json_writer.write(progress=None,status="failed",text=logStr)

        with open(electron_status_file, "w") as electron_status:
            print("weshop native status code: 2001", file = electron_status, flush= True)

        os._exit(0)

    
    with open(pid_json, "w", buffering=1) as file:
        json.dump({"pid":pid_list}, file)

    sidecar_pid, broker_pid = run_broker(loaded_model, vae)
    pid_list.append(sidecar_pid)
    pid_list.append(broker_pid)
    with open(pid_json, "w", buffering=1) as file:
        json.dump({"pid":pid_list}, file)

    progress_bar_json_writer.write(progress=90,status="pending",text="Start frontend server" if isEn else "开始启动前端")

    frontend_pid = run_frontend()
    pid_list.append(frontend_pid)
    frontend_result = check_frontend()
    if frontend_result == 0:
        terminate_processes(pid_list)
        with open(pid_json, "w", buffering=1) as file:
            json.dump({"pid":[]}, file)
        logStr = f"Start frontend failed" if isEn else f"前端启动失败"
        with open(start_log, "a",encoding="utf-8",buffering=1) as log:
            print("前端启动失败", file=log, flush=True)
        progress_bar_json_writer.write(progress=None,status="failed",text="前端启动失败")

        with open(electron_status_file, "w") as electron_status:
            print("weshop native status code: 2001", file = electron_status, flush= True)

        os._exit(0)

    with open(pid_json, "w", buffering=1) as file: 
        json.dump({"pid" : pid_list}, file)
   
    progress_bar_json_writer.write(progress=100,status="pending",text="Startup successful, ready to open the web page" if isEn else "启动成功,准备打开网页")

    url = f"http://127.0.0.1:{port_num}/workspace"
    times = 0
    while times < 30:
      response = requests.get(url)
      if response.status_code == 200:
          break
      else:
          times = times + 1
          progress_bar_json_writer.write(progress=100,status="pending",text="Startup successful, ready to open the web page" if isEn else f"启动成功,准备打开网页" + ("." * (times % 3)))
          time.sleep(1)

    with open(electron_status_file, "w") as electron_status:
        print("weshop native status code: 1001", file = electron_status, flush= True)
    progress_bar_json_writer.write(progress=100,status="success",text="Startup successful, opening the web page" if isEn else f"启动成功,正在打开网页" + ("." * (times % 3)))
    
    webbrowser.open(url)
