import logging
import os
import time
import yaml
import pymysql
from colors import Color
from plesk_migrator.sdk.utils import safe_format, safe_string_repr, parse_key_value_line


def write_yaml(filename, data):
    """
    :type filename: str | unicode
    :type data: any
    """
    log(u"Writing dump file")
    with open(filename, 'w') as dump_file:
        yaml.safe_dump(data, dump_file, encoding='utf-8', allow_unicode=True)
    log(safe_format(u"SUCCESS: '{file}' is ready", file=filename), Color.green)


def sql_to_dict(query, connection):
    """
    :type query: str | unicode | None
    :type connection: pymysql.connections.Connection
    :rtype: list
    """
    with connection.cursor(pymysql.cursors.DictCursor) as cursor:
        cursor.execute(query)
        result = cursor.fetchall()
    return result


def filter_list(somelist):
    """
    :type somelist: list
    :rtype: list
    """
    return list(
        value
        for value
        in somelist
        if value is not None and len(value) > 0
    )


def filter_dict(**kwargs):
    """
    :rtype: dict
    """
    return dict(
        (key, value)
        for key, value
        in kwargs.items()
        if isinstance(value, bool) or value is not None and len(value) > 0
    )


def uniq_list(seq):
    """
    :type seq: list
    :rtype: list
    """
    seen = set()
    seen_add = seen.add
    return [x for x in seq if not (x in seen or seen_add(x))]


def check_file(filename):
    """
    :type filename: str | unicode
    :rtype: str | unicode | None
    """
    if not os.path.isfile(filename):
        return safe_format(u"File '{path}' not found", path=filename)
    return None


def check_dir(directory):
    """
    :type directory: str | unicode
    :rtype: str | unicode | None
    """
    if not os.path.isdir(directory):
        return safe_format(u"Directory '{path}' not found", path=directory)
    return None


def check_document_root(path, warnings, text, result):
    """
    :type path: str | unicode | None
    :type warnings: dict
    :type text: str | unicode | None
    :type result: str | unicode | None
    :rtype: str | unicode | None
    """
    directory = path
    error = check_dir(directory)
    if error:
        warn(warnings, text+error, Color.yellow)
        return
    return result


def log(text, color=Color.default):
    """
    :type text: str | unicode
    :type color: str
    """
    try:
        print (
            Color.white +
            time.strftime("[%Y-%m-%d %H:%M:%S]") +
            ' ' +
            color +
            safe_string_repr(text).encode('utf-8') +
            Color.default
        )
    except Exception:
        print('Failed to print message:')
        logging.exception("Exception")


def warn(warnings, text, color=Color.default, display=True, wtype="default"):
    """
    :type warnings: dict
    :type text: str | unicode
    :type color: str
    :type display: bool
    :type wtype: str | unicode
    :rtype: str | unicode | None
    """
    if display:
        log(text, color)
    if wtype not in warnings:
        warnings[wtype] = []
    if wtype == "default":
        warnings[wtype].append({
            "text": text,
            "color": color,
        })
    else:
        warnings.update({
            wtype: {
                "text": text,
                "color": color
            }
        })
    return text


def show_warnings(warnings):
    """
    :type warnings: dict
    """
    if not warnings:
        return
    log("Printing warnings")
    if "default" in warnings:
        for warning in warnings["default"]:
            try:
                print (
                    warning.get("color", Color.default) +
                    safe_string_repr(warning.get("text", '')).encode('utf-8') +
                    Color.default
                )
            except Exception:
                print('Failed to print warning:')
                logging.exception("Exception")
    for warning_type, warning in warnings.items():
        if warning_type != "default":
            try:
                print (
                    warning.get("color", Color.default) +
                    safe_string_repr(warning.get("text", '')).encode('utf-8') +
                    Color.default
                )
            except Exception:
                print('Failed to print warning:')
                logging.exception("Exception")


def close_warn(warnings, warninig):
    """
    :type warnings: dict
    :type warninig: str | unicode | None
    """
    if warnings["default"][-1]["text"] == warninig:
        warnings["default"].pop()


def get_lines(warnings, filename, skip_error=False, skip_empty_strings=True):
    """
    :type warnings: dict
    :type filename: str | unicode
    :type skip_error: bool
    :type skip_empty_strings: bool
    :rtype: list
    """
    error = check_file(filename)
    if error:
        if not skip_error:
            warn(warnings, error, color=Color.yellow)
        return []
    try:
        lines = _get_file_lines(filename)
        result = []
        for line in lines:
            if skip_empty_strings and len(line.strip()) == 0:
                continue
            result.append(line.rstrip('\n'))
        return result
    except Exception as e:
        warn(
            warnings,
            safe_format(
                'Failed to get content from file "{file}": {exception}',
                file=filename,
                exception=e
            ),
            color=Color.red
        )
        return []


def _get_file_lines(filename):
    with open(filename) as f:
        return f.readlines()


def get_value(warnings, filename, key, skip_error=False, default_value=None):
    """Get value for defined key from file
    
    :type warnings: dict
    :type filename: str | unicode
    :type key: str | unicode
    :type skip_error: bool
    :type default_value: str | unicode | None
    :rtype: str | unicode | None
    """
    for line in get_lines(warnings, filename, skip_error):
        value = parse_key_value_line(line, key)
        if value is not None:
            return value
    if not skip_error:
        warn(
            warnings,
            safe_format(
                u"'{param}' parameter is not found in '{da_file}'",
                param=key,
                da_file=filename
            ),
            color=Color.yellow
        )
    return default_value


def get_file_content(warnings, filename, skip_error=False):
    """
    :type warnings: dict
    :type filename: str | unicode
    :type skip_error: bool
    :rtype: str | unicode | None
    """
    error = check_file(filename)
    if error:
        if not skip_error:
            warn(warnings, error, color=Color.yellow)
        return None
    try:
        return _get_file_content(filename)
    except Exception as e:
        warn(
            warnings,
            safe_format(
                'Failed to get content from file "{file}": {exception}',
                file=filename,
                exception=e
            ),
            color=Color.red
        )
        return None


def _get_file_content(filename):
    with open(filename) as f:
        return f.read()
