# -*- coding: utf-8 -*-

import os
import platform
import re
import sys
from typing import List
from ddans.data.color import DColor
from ddans.domain.format import Format
from ddans.native.hook import NHook
from ddans.native.log import NLog
from ddans.native.system import NSystem

if NSystem.is_darwin:
    import readline  # noqa: F401,E111
else:
    import ctypes  # noqa: F401,E111

CmdExit = "exit"
CmdReset = "reset"


class Terminal(object):

    @staticmethod
    def input(desc="", fc: int = None, date=True):
        try:
            text = Format.desc(desc, date=False, fc=fc)
            if date:
                return input(Format.date_tips(text)).strip()
            else:
                return input(text).strip()
        except KeyboardInterrupt:
            print("\n")
            return ""
        finally:
            pass

    @staticmethod
    def print(desc="", fc: int = None, date=False):
        text = Format.desc(desc, date=False, fc=fc)
        if date:
            return print(Format.date_tips(text))
        else:
            return print(text)

    @staticmethod
    def pause():
        return Terminal.input("Press any key to continue . . .")

    @staticmethod
    def is_exit(desc=""):
        return desc.lower() == "quit" or desc.lower() == "exit"

    @staticmethod
    def is_reset(desc=""):
        return desc.lower() == "reset"

    @staticmethod
    def is_cmd(desc=""):
        if Terminal.is_exit(desc):
            return True
        elif Terminal.is_reset(desc):
            return True
        else:
            return False

    @staticmethod
    def get_cmd(desc=""):
        if Terminal.is_exit(desc):
            return CmdExit
        elif Terminal.is_reset(desc):
            return CmdReset
        else:
            return ""

    @staticmethod
    def input_cmd(title: str,
                  map: dict = dict(),
                  tips: str = "选择有误, 请重新选择",
                  fc: int = DColor.yellow,
                  only_key=False):
        only_input = len(map) <= 0
        while True:
            _input = Terminal.input(title, fc)
            if Terminal.is_cmd(_input):
                return _input
            elif len(_input) <= 0:
                NLog.error(tips)
            elif map.get(_input) is not None:
                return map.get(_input)
            elif only_input and len(_input) > 0:
                return _input
            elif not only_key and _input in map.values():
                return _input
            else:
                NLog.error(tips)

    @staticmethod
    def select(title: str,
               options: List[str] = [],
               tips: str = "选择有误, 请重新选择",
               fc: int = DColor.yellow):
        _len = len(options)
        _title = f"{title}: "
        if _len > 0:
            lists = [f"{i+1}: {options[i]}" for i in range(_len)]
            _desc = Format.desc(f"({', '.join(lists)})", DColor.red)
            _title = f"{title} {_desc}: "
        while True:
            _input = Terminal.input(_title, fc)
            _num = int(_input) if _input.isdigit() else 0
            if Terminal.is_cmd(_input):
                return _input
            elif len(_input) <= 0:
                NLog.error(tips)
            elif _num > 0 and _num <= _len:
                return options[_num - 1]
            elif _input in options:
                return _input
            elif _len <= 0 and len(_input) > 0:
                return _input
            else:
                NLog.error(tips)

    @staticmethod
    def select_num(title: str,
                   options: List[str] = [],
                   tips: str = "选择有误, 请重新选择",
                   fc: int = DColor.yellow):
        cmd = Terminal.select(title, options, tips, fc)
        if cmd in options:
            return options.index(cmd) + 1
        else:
            if len(Terminal.get_cmd(cmd)) > 0:
                return 0
            else:
                return -1

    @staticmethod
    def select_ok(title: str,
                  tips: str = "选择有误, 请重新选择",
                  fc: int = DColor.yellow):
        options = ["否", "是"]
        cmd = Terminal.select(title, ["否", "是"], tips, fc)
        if cmd in options:
            return str(options.index(cmd))
        else:
            return cmd

    @staticmethod
    def input_text(title: str,
                   tips: str = "输入有误, 请重新输入",
                   pattern: str | re.Pattern[str] = None,
                   fc: int = DColor.yellow,
                   default: str = None):
        while True:
            _input = Terminal.input(title, fc)
            if Terminal.is_cmd(_input):
                return _input
            elif pattern is None:
                return _input
            elif len(_input) <= 0 and NHook.isvalid_str(default):
                return default
            elif (pattern is not None and len(pattern) > 0
                  and re.match(pattern, _input)):
                return _input
            else:
                NLog.error(tips)

    @staticmethod
    def system_cmd(cmd: str):
        if len(cmd) <= 0:
            return

        if NSystem.is_windows and cmd == 'clear':
            cmd = 'cls'
        os.system(cmd)

    @staticmethod
    def ensure_cmd(title: str = "确定执行该操作吗？"):
        _desc = Format.desc("(yes|no)", DColor.red)
        _title = f"{title} {_desc}: "
        return Terminal.input_cmd(
            _title, {
                "0": "exit",
                "1": "yes",
                "no": "exit",
                "yes": "yes",
                "n": "exit",
                "y": "yes"
            })

    @staticmethod
    def set_title(title: str):
        try:
            if not NHook.isvalid_str(title):
                return

            system = platform.system()
            if system == "Windows":
                ctypes.windll.kernel32.SetConsoleTitleW(f"{title}")
            elif system == "Darwin" or system == "Linux":
                sys.stdout.write(f"\033]0;{title}\007")
                # 修改 iTerm2 标签名称
                sys.stdout.write(f"\033]1;{title}\007")
                sys.stdout.flush()
            else:
                pass
        except Exception:
            pass
