import os
import ftplib
import re
import socket

from .config import config
from time import sleep
from random import randint
from datetime import datetime


def get_serial_raspberry():
    """
    Returns a (unique) serial no. of the Raspberry Pi.
    Returns '0000000000000000' if /proc/cpuinfo does not contain a serial
    no. in the expected format.
    """
    # Extract serial from cpuinfo file
    cpuserial = '0000000000000000'
    with open('/proc/cpuinfo', 'r') as f:
        for line in f:
            if line[0:6] == 'Serial':
                cpuserial = line[10:26]
                return cpuserial

    return cpuserial


def ftp_get_file(file_remote, file_local):
    """
    Download ``file_remote`` and write it to ``file_local``.
    Rename local file to ``file_local`` only after download has completed
    to avoid race conditions.

    Args:
        file_remote: remote filename
        file_local: local filename

    Raises:
        FileNotFoundError if either local or remote file was not found
    """
    sess = ftp_start()
    file_local_tmp = file_local + ".tmp"
    try:
        sess.retrbinary('RETR {}'.format(file_remote),
                        open(file_local_tmp, 'wb').write)
    except ftplib.error_perm as e:
        raise FileNotFoundError("'{}' not found on FTP server"
                                .format(file_remote))
    else:
        os.rename(file_local_tmp, file_local)
    finally:
        sess.quit()
        sess.close()


def ftp_list_files(remote_dir, regex=None):
    """
    Get a list of files in a remote ftp directory.

    Args:
        remote_dir: remote directory
        regex: only list files that match this regex

    Returns:
        list of file names (excluding "." and "..")
    """
    sess = ftp_start()

    files = []
    try:
        for f, facts in sess.mlsd(path=remote_dir):
            if f == "." or f == "..":
                continue
            if regex is None:
                files.append(f)
            else:
                if re.match(regex, f):
                    files.append(f)
    finally:
        sess.quit()
        sess.close()

    return files


def ftp_put_file(file_local, file_remote):
    """
    Args:
        file_local: path of local file to be uploaded
        file_remote: path of remote file
    """
    sess = ftp_start()

    # sess.debug(2)
    # sess.set_debuglevel(2)
    # sess.set_pasv(True)

    try:
        with open(file_local, "rb") as f:
            sess.storbinary("STOR {}".format(file_remote), f)
    finally:
        sess.quit()
        sess.close()


def ftp_delete_file(file_remote):
    """
    Delete a file from the FTP server

    Args:
        file_remote: path of remote file to be deleted
    """
    sess = ftp_start()

    try:
        sess.delete(file_remote)
    except (ftplib.error_perm, ftplib.error_reply) as e:
        raise e
    finally:
        sess.quit()
        sess.close()


def ftp_start():
    """
    Connect to ftp server and log in
    Retries the login 5 times in arbitrary intervals between 1 and 10 seconds

    Raises:
        ``OSError`` if DNS resolver fails (socket.gaierror)
    """
    username = config.get("GENERAL", "ftp_username")
    password = config.get("GENERAL", "ftp_password")
    host = config.get("GENERAL", "ftp_host")

    for x in range(0, 5):
        try:
            sess = ftplib.FTP(host)
            sess.login(username, password)
            return sess
        except ftplib.error_temp as e:
            nowString = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
            print("{dt} FTP Login failed on attempt #{idx}"
                  .format(dt=nowString, idx=x))
            print("Error message: {0}".format(e))
            sleep(randint(1, 9))
        except socket.gaierror as e:
            raise OSError("could not resolve host name {}: {}"
                          .format(host, e))
    return
