# -*- coding: utf-8 -*-
import codecs
import os

from zuper_commons.types import check_isinstance
from . import logger
from .zc_friendly_path import friendly_path
from .zc_mkdirs import make_sure_dir_exists
from .zc_path_utils import expand_all

__all__ = [
    'read_bytes_from_file',
    'read_ustring_from_utf8_file',
    'read_ustring_from_utf8_file_lenient',
    'write_bytes_to_file',
    'write_ustring_to_utf8_file',
]


def read_bytes_from_file(filename: str) -> bytes:
    """ Read binary data and returns bytes """
    _check_exists(filename)
    with open(filename, 'rb') as f:
        return f.read()


def read_ustring_from_utf8_file(filename: str) -> str:
    """ Returns a unicode/proper string """
    _check_exists(filename)
    with codecs.open(filename, encoding='utf-8') as f:
        try:
            return f.read()
        except UnicodeDecodeError as e:
            msg = 'Could not successfully decode file %s' % filename
            raise UnicodeError(msg) from e


def read_ustring_from_utf8_file_lenient(filename) -> str:
    """ Ignores errors """
    _check_exists(filename)
    with codecs.open(filename, encoding='utf-8', errors='ignore') as f:
        try:
            return f.read()
        except UnicodeDecodeError as e:
            msg = 'Could not successfully decode file %s' % filename
            raise UnicodeError(msg) from e


def _check_exists(filename):
    if not os.path.exists(filename):
        if os.path.lexists(filename):
            msg = 'The link %s does not exist.' % filename
            msg += ' it links to %s' % os.readlink(filename)
            raise ValueError(msg)
        else:
            msg = 'Could not find file %r' % filename
            msg += ' from directory %s' % os.getcwd()
            raise ValueError(msg)


def write_ustring_to_utf8_file(data: str, filename, quiet=False):
    """
    It also creates the directory if it does not exist.
    :param data:
    :param filename:
    :param quiet:
    :return:
    """
    check_isinstance(data, str)
    b = data.encode('utf-8')  # OK
    return write_bytes_to_file(b, filename, quiet=quiet)


def write_bytes_to_file(data: bytes, filename: str, quiet=False):
    """
        Writes the data to the given filename.
        If the data did not change, the file is not touched.

    """
    check_isinstance(data, bytes)

    L = len(filename)
    if L > 1024:
        msg = f'Invalid argument filename: too long at {L}. Did you confuse it with data?\n{filename[:1024]}'
        raise ValueError(msg)

    filename = expand_all(filename)
    make_sure_dir_exists(filename)

    if os.path.exists(filename):
        current = open(filename, 'rb').read()
        if current == data:
            if not 'assets' in filename:
                if not quiet:
                    logger.debug('already up to date %s' % friendly_path(filename))
            return

    with open(filename, 'wb') as f:
        f.write(data)

    if filename.startswith('/tmp'):
        quiet = True

    if not quiet:
        size = '%.1fMB' % (len(data) / (1024 * 1024))
        logger.debug('Written %s to: %s' % (size, friendly_path(filename)))
