# -*- coding: utf-8 -*-
import json
import os
import shutil
import platform
import subprocess
from typing import List, Union

# from ddans.descriptor.static import StaticProperty
from ddans.domain.regex import Regex
from ddans.native.hook import NHook

root = os.getcwd()

ContentType = Union[str, bytes, dict, list]

system = platform.system()

isWindows = system == "Windows"
isLinux = system == "Linux"
isDarwin = system == "Darwin"

if isWindows:
    import winreg  # noqa: F401,E111

if isWindows:
    platformShort = "W"
elif isDarwin:
    platformShort = "M"
elif isLinux:
    platformShort = "L"
else:
    platformShort = ""


class NSystem():
    is_windows = isWindows
    is_linux = isLinux
    is_darwin = isDarwin
    platform_short = platformShort

    @staticmethod
    def get_root():
        return root

    @staticmethod
    def reset_root():
        return NSystem.set_current_path(root)

    @staticmethod
    def get_current_path():
        return os.getcwd()

    @staticmethod
    def set_current_path(path=''):
        os.chdir(path)

    @staticmethod
    def get_full_path(path, *args):
        return os.path.abspath(NSystem.join(path, *args))

    @staticmethod
    def join(path, *args):
        return os.path.join(path, *args)

    @staticmethod
    def dirname(path):
        return os.path.dirname(path)

    @staticmethod
    def exists(dirOrFile):
        return os.path.exists(dirOrFile)

    @staticmethod
    def get_dir(path):
        arr = os.path.split(path)
        dir = arr[0] or '.'
        file = arr[1]
        if len(file) <= 0:
            return dir
        elif "." not in file:
            return path
        return dir

    @staticmethod
    def ensure_dir(path="./", direct=False):
        dir = path if direct else NSystem.get_dir(path)
        _dir = NSystem.get_full_path(dir)
        if not os.path.exists(_dir):
            os.makedirs(_dir)

    @staticmethod
    def clean_dir(path):
        dir = NSystem.get_dir(path)
        if os.path.exists(dir):
            shutil.rmtree(dir)
            os.makedirs(dir)

    @staticmethod
    def split_name_ext(file: str):
        if len(file) <= 0:
            return "", ""
        tmp = os.path.splitext(file)
        name = os.path.basename(tmp[0])
        ext = tmp[1][1:].lower()
        return name, ext

    @staticmethod
    def isvalid_ext(name, ext: Union[str, List[str]]):
        if not NHook.isvalid_str(name):
            return False

        _, _ext = NSystem.split_name_ext(name)
        if not NHook.isvalid_str(_ext):
            return False

        exts = []
        if isinstance(ext, str):
            if NHook.isvalid_str(ext):
                exts = [ext]
            else:
                return False
        elif isinstance(ext, list):
            exts = ext
        else:
            return False

        lists = [
            item.lower() for item in exts if isinstance(item, str)
            and item.strip() != '' and exts.count(item) == 1
        ]

        count = len(lists)
        if count <= 0:
            return False
        return _ext.lower() in lists

    @staticmethod
    def listdir(path: str):
        try:
            names = os.listdir(path)
            return [NSystem.join(path, name) for name in names]
        except Exception:
            return []

    @staticmethod
    def basename(file: str):
        return os.path.basename(file)

    @staticmethod
    def get_directory_names(path):
        """
        获取路径名称列表

        倒叙名称，可能需要 reversed 反转
        """
        names = []
        try:
            while True:
                path, tail = os.path.split(path)
                if tail:
                    names.append(tail)
                else:
                    # 特殊处理Windows根目录，获取盘符的字母部分
                    if os.name == 'nt':
                        if path.endswith(':\\'):
                            names.append(path.replace(':\\', ''))
                        else:
                            names.append(path)
                    else:
                        # 特殊处理macOS和Linux根目录
                        if path == '/':
                            names.append('/')
                        else:
                            names.append(os.path.basename(path))
                    break
            # reversed(dirNames[:2])
            return names
        except Exception:
            return []

    @staticmethod
    def get_home():
        return os.path.expanduser('~')

    @staticmethod
    def get_sys_download():
        return os.path.expanduser('~/Downloads')

    @staticmethod
    def get_sys_document():
        return os.path.expanduser('~/Documents')

    @staticmethod
    def get_path():
        home = NSystem.get_home()
        dir = NSystem.get_full_path(home, ".ddan")
        NSystem.ensure_dir(dir, True)
        return dir

    @staticmethod
    def get_app_data():
        if isWindows:
            return os.path.expanduser('~/AppData/Roaming')
        elif isDarwin:
            return os.path.expanduser('~/Library/Application Support')
        elif isLinux:
            return os.path.expanduser('~/.config')
        else:
            return None

    @staticmethod
    def get_etc_path():
        if isWindows:
            return os.path.join(os.environ.get("SystemRoot", "C:\\Windows"),
                                "System32", "drivers", "etc")
        else:
            return "/etc"

    @staticmethod
    def open_folder(path):
        if not NSystem.exists(path):
            return False
        if isWindows:
            os.startfile(f"{path}")  # 在 Windows 上使用 startfile 函数打开文件夹
            return True
        elif isDarwin:
            os.system(f"open '{path}'")  # 在 macOS 上使用 open 命令打开文件夹
            return True
        elif isLinux:
            os.system(f"xdg-open '{path}'")  # 在 Linux 上使用 xdg-open 命令打开文件夹
            return True
        else:
            return False

    @staticmethod
    def system(cmd: str):
        if not NHook.isvalid_str(cmd):
            return
        os.system(cmd)

    @staticmethod
    def exec(*args):
        try:
            _args = NHook.to_list(args)
            result = subprocess.run(_args,
                                    stdout=subprocess.PIPE,
                                    stderr=subprocess.PIPE,
                                    text=True,
                                    check=True)
            return result.stdout.strip()
        except subprocess.CalledProcessError:
            # 捕捉到 git 命令错误时返回 None
            return None

    @staticmethod
    def run(*args):
        try:
            _args = NHook.to_list(args)
            return subprocess.run(_args,
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.PIPE,
                                  text=True,
                                  check=True)
        except subprocess.CalledProcessError:
            # 捕捉到 git 命令错误时返回 None
            return None

    @staticmethod
    def check_output(*args):
        try:
            _args = NHook.to_list(args)
            result = subprocess.check_output(_args, text=True, shell=True)
            return result.strip()
        except subprocess.CalledProcessError as e:
            # 捕捉到 git 命令错误时返回 None
            print(f"Error occurred: {e}")
            return None
        except KeyboardInterrupt as e:
            print(f"Error occurred: {e}")
            return None
        except Exception as e:
            print(f"Error occurred: {e}")
            return None

    @staticmethod
    def shellex(cmd: str = ""):
        try:
            subprocess.run(cmd, text=True, check=True, shell=True)
            return True
        except subprocess.CalledProcessError:
            return False
        except KeyboardInterrupt:
            return False
        except Exception:
            return False

    @staticmethod
    def shell(cmd: str = "") -> bool:
        try:
            # 使用 Popen 启动子进程，并创建新的进程组
            process = subprocess.Popen(cmd, shell=True)
            process.wait()  # 等待子进程完成
            return process.returncode == 0  # 成功执行返回 True
        except subprocess.CalledProcessError:
            return False
        except KeyboardInterrupt:
            if process is not None and process.poll() is not None:
                process.kill()
                process.wait()
            return False
        except Exception:
            return False

    @staticmethod
    def open_terminal():
        current_path = os.getcwd()
        if isWindows:
            # Windows-specific command
            command = f'start cmd /K "cd /d {current_path}"'
        elif isLinux:
            # Linux-specific command (assuming gnome-terminal)
            command = (f'gnome-terminal -- bash -c "cd \\"{current_path}\\";'
                       f' exec bash"')
            # If using xterm, use the following command instead:
            # command = f'xterm -e "cd \\"{current_path}\\"; exec bash"'
        elif isDarwin:  # macOS is identified as "Darwin"
            # macOS-specific command using osascript
            script = f'''
            tell application "Terminal"
                do script "cd \\"{current_path}\\"; exec bash"
                activate
            end tell
            '''
            command = ["osascript", "-e", script]
        else:
            raise OSError(f"Unsupported operating system: {system}")

        # Execute the command
        if isDarwin:
            subprocess.Popen(command)
        else:
            subprocess.Popen(command, shell=True)

    @staticmethod
    def isdir(path):
        if not NHook.isvalid_str(path):
            return False
        return os.path.isdir(path)

    @staticmethod
    def isfile(path):
        if not NHook.isvalid_str(path):
            return False
        return os.path.isfile(path)

    @staticmethod
    def getsize(path):
        if not NSystem.exists(path):
            return 0
        return os.path.getsize(path)

    @staticmethod
    def read_data(file):
        try:
            res = None
            if not NSystem.isfile(file):
                return None
            with open(file, 'rb') as fs:
                # 从文件中读取数据
                res = fs.read()
            return res
        except Exception:
            return None

    @staticmethod
    def read_json(path, default=dict()):
        try:
            with open(path, 'r', encoding='utf8') as fp:
                map = json.load(fp)
            return map
        except IOError:
            # print('read_json ' + path + 'error', error)
            return default
        except Exception:
            return default
        # finally:
        # return map

    @staticmethod
    def write_json(dict_data, path, minify=False):
        try:
            NSystem.ensure_dir(path)
            with open(path, 'w', encoding='utf8', newline='') as fp:
                if minify:
                    fp.write(
                        json.dumps(dict_data,
                                   ensure_ascii=False,
                                   separators=(',', ':')))
                else:
                    fp.write(
                        json.dumps(dict_data, ensure_ascii=False, indent=2))
                return True
        except IOError as error:
            print('write_json ' + path + 'error', error)
            # print('')
            return False

    @staticmethod
    def write_file(buffer: ContentType, filename: str):
        if not NHook.isvalid_str(filename):
            return None
        if not NHook.isvalid(buffer, (str, bytes, dict, list)):
            return None
        _buffer = buffer
        if isinstance(buffer, str):
            _buffer = buffer.encode("utf8")
        elif isinstance(buffer, dict):
            content = json.dumps(buffer, ensure_ascii=False, indent=2)
            _buffer = content.encode("utf8")
        elif isinstance(buffer, list):
            content = str(buffer)
            _buffer = content.encode("utf8")
        else:
            return None

        fullname = NSystem.get_full_path(filename)
        NSystem.ensure_dir(fullname)
        with open(fullname, 'wb') as fs:
            fs.write(_buffer)
            fs.flush()
            fs.close()
        return fullname

    @staticmethod
    def copy(source: str, target: str):
        try:
            if not NHook.isvalid_str(source) or not NHook.isvalid_str(target):
                return None

            source_file = NSystem.get_full_path(source)
            target_path = NSystem.get_full_path(target)
            return shutil.copy2(source_file, target_path)
        except IOError:
            return None
        except Exception:
            return None

    @staticmethod
    def copy_file(source: str, target: str):
        try:
            if not NHook.isvalid_str(source) or not NHook.isvalid_str(target):
                return None

            source_file = NSystem.get_full_path(source)
            target_file = NSystem.get_full_path(target)
            NSystem.ensure_dir(target_file)
            return shutil.copyfile(source_file, target_file)
        except IOError:
            return None
        except Exception:
            return None

    @staticmethod
    def remove_file(filepath: str):
        try:
            if not NHook.isvalid_str(filepath):
                return None

            if not NSystem.exists(filepath):
                return None
            os.remove(filepath)
            return True
        except IOError:
            return None
        except Exception:
            return None

    @staticmethod
    def get_proxy():
        try:
            if isWindows:
                sub_key = ("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\"
                           "Internet Settings")
                registry_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
                                              rf"{sub_key}")

                # 获取代理设置的值
                proxy_enable, _ = winreg.QueryValueEx(registry_key,
                                                      "ProxyEnable")
                proxy_server, _ = winreg.QueryValueEx(registry_key,
                                                      "ProxyServer")
                winreg.CloseKey(registry_key)
                if proxy_enable == 1 and NHook.isvalid_str(proxy_server):
                    return proxy_server
            elif isDarwin:
                # 执行 `scutil` 命令以获取代理设置
                result = NSystem.check_output(
                    'networksetup -getwebproxy "Wi-Fi"')
                output = result

                # 解析输出，提取代理设置
                proxies = {}
                for line in output.splitlines():
                    if line:
                        key, value = line.split(": ", 1)
                        proxies[key] = value

                proxy_enable = proxies.get("Enabled")
                proxy_server = proxies.get("Server")
                if proxy_enable == 'Yes' and NHook.isvalid_str(proxy_server):
                    return f"{proxy_server}:{proxies.get('Port', 0)}"
            elif isLinux:
                # 执行 `scutil` 命令以获取代理设置
                result = NSystem.check_output('env | grep https_proxy')
                if not NHook.isvalid_str(result):
                    return None
                output = result.strip()

                match = Regex.addrport.search(output)

                if match:
                    proxy_server = match.group(1)  # 第一个捕获组匹配 IP 地址
                    port = match.group(2)  # 第二个捕获组匹配端口号
                    return f"{proxy_server}:{port}"
            return None
        except Exception:
            return None
