import logging
import logging.config
import logging.handlers
import os
import sys
import json

import persistence
import unicodedata
from enum import Enum
from customencodings import custom_encode
from typing import (
    Union,
    Optional,
)


def config_logger(
    loader_path,                    # type: str
    log_name='component',           # type: Optional[str]
    configure_root=False,           # type: Optional[bool]
    root_level=logging.WARN,        # type: Optional[int]
    max_files=5,                    # type: Optional[int]
    max_size=5242880,               # type: Optional[int]
    add_error_handler=False,        # type: Optional[bool]
):
    # type: (...) -> None

    real_path = os.path.realpath(loader_path)
    if os.path.isfile(real_path) and real_path.endswith(".ini"):
        logging.config.fileConfig(real_path, disable_existing_loggers=False)
        return

    if os.path.isfile(real_path) and real_path.endswith(".json"):
        with open(real_path, "rb") as f:
            logging.config.dictConfig(json.loads(f.read().decode("utf-8")))
        return

    if os.path.isdir(real_path):
        dir_path = real_path
    else:
        dir_path = os.path.dirname(real_path)

    filename = os.path.join(dir_path, log_name + '.log')
    filename_error = os.path.join(dir_path, log_name + '.error.log')

    log_string = "%(asctime)-6s: %(name)s - %(levelname)s - %(thread)d - %(threadName)s - %(message)s"
    formatter = logging.Formatter(log_string)

    comp_logger = logging.getLogger(log_name)
    comp_logger.setLevel(logging.DEBUG)
    comp_logger.propagate = False

    rotating_file_logger = logging.handlers.RotatingFileHandler(filename, 'a', max_size, max_files)
    rotating_file_logger.setLevel(logging.DEBUG)
    rotating_file_logger.setFormatter(formatter)
    comp_logger.addHandler(rotating_file_logger)

    if add_error_handler:
        rotating_file_logger_error = logging.handlers.RotatingFileHandler(filename_error, 'a', max_size, max_files)
        rotating_file_logger_error.setLevel(logging.ERROR)
        rotating_file_logger_error.setFormatter(formatter)
        comp_logger.addHandler(rotating_file_logger_error)

    if configure_root:
        root_logger = logging.getLogger()
        root_logger.addHandler(rotating_file_logger)
        root_logger.setLevel(root_level)


def import_pydevd(loader_path, port, suspend=False):
    # type: (str, int, bool) -> None
    if "DEBUG" in os.environ:
        dir_path = os.path.dirname(os.path.realpath(loader_path))
        filename = os.path.join(dir_path, "debug.txt")
        if os.path.isfile(filename):
            with open(filename) as debug_file:
                debug_path = debug_file.read()
            if os.path.exists(debug_path):
                if debug_path not in sys.path:
                    sys.path.append(debug_path)
                try:
                    import pydevd
                    pydevd.settrace('localhost', port=port, stdoutToServer=True, stderrToServer=True, suspend=suspend)
                except ImportError:
                    pydevd_using_debug_lib(
                        loader_path=loader_path,
                        port=port,
                        suspend=suspend,
                    )
            else:
                debug_path = os.path.abspath(debug_path)
                print(debug_path)
                print("path errado")


def pydevd_using_debug_lib(
    loader_path,        # type: str
    port,               # type: int
    suspend=False,      # type: Optional[bool]
):
    # type: (...) -> None

    try:
        current_dir = os.path.dirname(os.path.abspath(__file__))
        debug_lib_dir = os.path.join(current_dir, "..", "debug_lib")
        if os.path.exists(debug_lib_dir):
            sys.path.append(debug_lib_dir)
            from debughelper import import_pydevd as real_import_pydevd
            real_import_pydevd(loader_path, port, suspend)
    except ImportError:
        print("error configure debug, run install_lib_debug.sh")
        return


def get_valid_pricelists(mbcontext):
    price_lists = []

    conn = None
    try:
        conn = persistence.Driver().open(mbcontext)
        cursor = conn.select("SELECT PriceListID FROM Pricelist WHERE CURRENT_DATE BETWEEN EnabledFrom AND EnabledThru")
        for row in cursor:
            pl_id = str(row.get_entry(0) or '')
            price_lists.append(pl_id)
    finally:
        if conn:
            conn.close()
    return price_lists


def unicode_2_ascii(data):
    import unicodedata

    try:
        data = data.decode('latin_1', 'ignore')
    except Exception: # noqa
        pass
    finally:
        data = unicode(data)
        data = unicodedata.normalize('NFKD', data)
        data = data.encode('ascii', 'ignore')

    return data


def remove_accents(text):
    # type: (Union[str, unicode]) -> str

    if not text:
        return text

    try:
        text = unicode(text, 'utf-8')
    except (TypeError, NameError):
        pass

    text = unicodedata.normalize('NFD', text)
    text = text.encode('ascii', 'ignore')
    return text


def encode_cp860(
    text    # type: str
):
    # type: (...) -> str

    try:
        unicode_text = unicode(text, "utf-8")
    except TypeError:
        unicode_text = text

    try:
        response_text = unicode_text.encode("cp860")
    except TypeError:
        response_text = remove_accents(unicode_text)

    return response_text


def custom_encode_cp860(
    text,    # type: str
):
    # type: (...) -> str

    try:
        return custom_encode(
            text=text,
        )
    except Exception as ex:
        return remove_accents(
            text=text,
        )


def format_cpf_cnpj(value):
    formatted_value = value
    try:
        if len(value) == 11:
            value_to_format = value.zfill(11)
            formatted_value = '{}.{}.{}-{}'.format(value_to_format[:3],
                                                   value_to_format[3:6],
                                                   value_to_format[6:9],
                                                   value_to_format[9:])
        elif len(value) == 14:
            value_to_format = value.zfill(14)
            formatted_value = '{}.{}.{}/{}-{}'.format(value_to_format[:2],
                                                      value_to_format[2:5],
                                                      value_to_format[5:8],
                                                      value_to_format[8:12],
                                                      value_to_format[12:])
    except Exception as _:
        return value

    return formatted_value


class ExtendedEnum(Enum):

    @classmethod
    def list_values(cls):
        # type: () -> list
        return list(map(lambda enum: enum.value, cls))

    @classmethod
    def list_names(cls):
        # type: () -> list
        return list(map(lambda enum: enum.name, cls))

    @classmethod
    def list_enums_tuple(cls):
        # type: () -> list
        return list(map(lambda enum: (enum.name, enum.value), cls))

    @classmethod
    def get_name(cls, value):
        # type: (int) -> Union[str, None]
        if value not in cls.list_values():
            return None

        return next(str(enum.name) for enum in cls if enum.value == value)

    @classmethod
    def get_value(cls, name):
        # type: (str) -> Union[int, None]
        if name not in cls.list_names():
            return None

        return next(int(enum.value) for enum in cls if enum.name == name)


class MwOrderStatus(ExtendedEnum):
    IN_PROGRESS = 1
    STORED = 2
    TOTALED = 3
    VOIDED = 4
    PAID = 5
    RECALLED = 6
