import sys
import socket
import hashlib
import subprocess

from datetime import datetime, timedelta
from os.path import join

from . import UbacBase, logger, logger_check, parse_list


class UbacRsync(UbacBase):
    DEST_SUBDIR = 'rsync'

    def __init__(self, destination, paths, freshness=24, nb_to_keep=7):
        super(UbacRsync, self).__init__(
            destination, freshness=freshness, nb_to_keep=nb_to_keep
        )

        self.paths = paths

    @classmethod
    def from_section(cls, section, remote):
        return cls(
            remote,
            list(parse_list(section['fs_paths'])),
            freshness=int(section['check_since']),
            nb_to_keep=int(section['nb_to_keep']),
        )

    def backup_all(self):
        for path in self.paths:
            try:
                self.single_path(path)
                self.clean(path)
            except KeyboardInterrupt:
                sys.exit(-1)
            except:
                logger.exception('Error backuping fs path %s', path)

    def ressource_from_path(self, path):
        return hashlib.sha1(path.encode()).hexdigest()

    def single_path(self, path):
        logger.info('backuping path %s', path)
        ressource = self.ressource_from_path(path)

        instant = datetime.utcnow().strftime('%Y%m%d_%H%M%S')

        destination = join(
            self.destination.dest_base_path,
            socket.gethostname(),
            self.DEST_SUBDIR,
            ressource,
            instant,
        )

        backups = self.destination.list(
            socket.gethostname(), self.DEST_SUBDIR, ressource
        )
        if backups:
            last = sorted(backups, reverse=True)[0]
            source = join(
                self.destination.dest_base_path,
                socket.gethostname(),
                self.DEST_SUBDIR,
                ressource,
                last
            )

            self.destination.ssh_exec('cp -al {} {}'.format(
                source, destination
            ))
        else:
            self.destination.mkdir(
                socket.gethostname(), self.DEST_SUBDIR, ressource, instant
            )

        command = 'rsync -az --delete -e'.split()
        command += ['ssh -p %d' % (self.destination.ssh_port)]
        command += [path + '/', '%s@%s:%s/' % (
            self.destination.ssh_user, self.destination.ssh_host, destination
        )]
        subprocess.check_call(command)

    def check_all(self):
        result = True
        for path in self.paths:
            try:
                self.check(path)
            except AssertionError as e:
                logger_check.error(*e.args)
                result = False
        return result

    def check(self, path):
        ressource = self.ressource_from_path(path)
        backups = self.destination.list(
            socket.gethostname(), self.DEST_SUBDIR, ressource
        )
        assert backups, 'No backup found for path %s' % (path)
        last = backups[-1]
        last_date = datetime.strptime(last, '%Y%m%d_%H%M%S')
        last_age = datetime.utcnow() - last_date
        assert last_age < timedelta(hours=self.freshness), (
            'last backup for path %s is %s' % (path, last_age)
        )
        logger.info('Backup of path %s is fresh enough', path)
        return True

    def clean(self, path):
        ressource = self.ressource_from_path(path)
        backups = self.destination.list(
            socket.gethostname(), self.DEST_SUBDIR, ressource
        )
        for to_del in sorted(backups, reverse=True)[self.nb_to_keep:]:
            logger.info(
                'Removing backup %s of %s %s',
                to_del, self.DEST_SUBDIR, path
            )
            destination = join(
                self.destination.dest_base_path,
                socket.gethostname(),
                self.DEST_SUBDIR,
                ressource,
                to_del,
            )
            self.destination.ssh_exec('chmod u+w -R {}'.format(destination))
            self.destination.remove_backup(
                socket.gethostname(), self.DEST_SUBDIR, ressource, to_del
            )
