# coding=utf-8
# Author: Nic Wolfe <nic@wolfeden.ca>
# URL: http://code.google.com/p/sickbeard/
#
# This file is part of SickRage.
#
# SickRage is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# SickRage is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with SickRage. If not, see <http://www.gnu.org/licenses/>.

import datetime
import os
import platform
import re
import shutil
import stat
import subprocess
import tarfile
import time
import traceback

import sickbeard
from sickbeard import db, helpers, logger, notifiers, ui
from sickrage.helper.encoding import ek
from sickrage.helper.exceptions import ex


class CheckVersion(object):
    """
    Version check class meant to run as a thread object with the sr scheduler.
    """

    def __init__(self):
        self.updater = None
        self.install_type = None
        self.amActive = False
        if sickbeard.gh:
            self.install_type = self.find_install_type()
            if self.install_type == 'git':
                self.updater = GitUpdateManager()
            elif self.install_type == 'source':
                self.updater = SourceUpdateManager()

        self.session = helpers.make_session()

    def run(self, force=False):

        self.amActive = True

        if self.updater:
            # set current branch version
            sickbeard.BRANCH = self.get_branch()

            if self.check_for_new_version(force):
                if sickbeard.AUTO_UPDATE:
                    logger.log(u"New update found for SickRage, starting auto-updater ...")
                    ui.notifications.message(_('New update found for SickRage, starting auto-updater'))
                    if self.run_backup_if_safe():
                        if sickbeard.versionCheckScheduler.action.update():
                            logger.log(u"Update was successful!")
                            ui.notifications.message(_('Update was successful'))
                            sickbeard.events.put(sickbeard.events.SystemEvent.RESTART)
                        else:
                            logger.log(u"Update failed!")
                            ui.notifications.message(_('Update failed!'))

            self.check_for_new_news()

        self.amActive = False

    def run_backup_if_safe(self):
        return self.safe_to_update() is True and self._runbackup() is True

    def _runbackup(self):
        # Do a system backup before update
        logger.log(u"Config backup in progress...")
        ui.notifications.message(_('Backup'), _('Config backup in progress...'))
        try:
            backupDir = ek(os.path.join, sickbeard.DATA_DIR, 'backup')
            if not ek(os.path.isdir, backupDir):
                ek(os.mkdir, backupDir)

            if self._keeplatestbackup(backupDir) and self._backup(backupDir):
                logger.log(u"Config backup successful, updating...")
                ui.notifications.message(_('Backup'), _('Config backup successful, updating...'))
                return True
            else:
                logger.log(u"Config backup failed, aborting update", logger.ERROR)
                ui.notifications.message(_('Backup'), _('Config backup failed, aborting update'))
                return False
        except Exception as e:
            logger.log(u'Update: Config backup failed. Error: {0}'.format(ex(e)), logger.ERROR)
            ui.notifications.message(_('Backup'), _('Config backup failed, aborting update'))
            return False

    @staticmethod
    def _keeplatestbackup(backupDir=None):
        if not backupDir:
            return False

        from sickrage.helper import glob
        files = glob.glob(ek(os.path.join, glob.escape(backupDir), '*.zip'))
        if not files:
            return True

        now = time.time()
        newest = files[0], now - ek(os.path.getctime, files[0])
        for f in files[1:]:
            age = now - ek(os.path.getctime, f)
            if age < newest[1]:
                newest = f, age
        files.remove(newest[0])

        for f in files:
            ek(os.remove, f)

        return True

    @staticmethod
    def _backup(backupDir=None):
        if not backupDir:
            return False
        source = [
            ek(os.path.join, sickbeard.DATA_DIR, 'sickbeard.db'),
            sickbeard.CONFIG_FILE,
            ek(os.path.join, sickbeard.DATA_DIR, 'failed.db'),
            ek(os.path.join, sickbeard.DATA_DIR, 'cache.db')
        ]
        target = ek(os.path.join, backupDir, 'sickrage-' + time.strftime('%Y%m%d%H%M%S') + '.zip')

        for (path, dirs, files) in ek(os.walk, sickbeard.CACHE_DIR, topdown=True):
            for dirname in dirs:
                if path == sickbeard.CACHE_DIR and dirname not in ['images']:
                    dirs.remove(dirname)
            for filename in files:
                source.append(ek(os.path.join, path, filename))

        return helpers.backupConfigZip(source, target, sickbeard.DATA_DIR)

    def safe_to_update(self):

        def db_safe(self):
            message = {
                'equal': {
                    'type': logger.DEBUG,
     
               'text': u"We can proceed with the update. New update has same DB version"},
                'upgrade': {
                    'type': logger.WARNING,
                    'text': u"We can't proceed with the update. New update has a new DB version. Please manually update"},
                'downgrade': {
                    'type': logger.ERROR,
                    'text': u"We can't proceed with the update. New update has a old DB version. It's not possible to downgrade"},
    
        }
            try:
                result = self.getDBcompare()
                if result in message:
                    logger.log(message[result]['text'], message[result]['type'])  # unpack the result message into a log entry
                else:
                    logger.log(u"We can't proceed with the update. Unable to check remote DB version. Error: {0}".format(result), logger.ERROR)
                return result in ['equal']  # add future True results to the list
            except Exception as error:
                logger.log(u"We can't proceed with the update. Unable to compare DB version. Error: {0}".format(repr(error)), logger.ERROR)
                return False

        def postprocessor_safe():
            if not sickbeard.autoPostProcessorScheduler.action.amActive:
                logger.log(u"We can proceed with the update. Post-Processor is not running", logger.DEBUG)
                return True
            else:
                logger.log(u"We can't proceed with the update. Post-Processor is running", logger.DEBUG)
                return False

        def showupdate_safe():
            if not sickbeard.showUpdateScheduler.action.amActive:
                logger.log(u"We can proceed with the update. Shows are not being updated", logger.DEBUG)
                return True
            else:
                logger.log(u"We can't proceed with the update. Shows are being updated", logger.DEBUG)
                return False

        db_safe = db_safe(self)
        postprocessor_safe = postprocessor_safe()
        showupdate_safe = showupdate_safe()

        if db_safe and postprocessor_safe and showupdate_safe:
            logger.log(u"Proceeding with auto update", logger.DEBUG)
            return True
        else:
            logger.log(u"Auto update aborted", logger.DEBUG)
            return False

    def getDBcompare(self):
        try:
            self.updater.need_update()
            cur_hash = str(self.updater.get_newest_commit_hash())
            assert len(cur_hash) == 40, "Commit hash wrong length: {0} hash: {1}".format(len(cur_hash), cur_hash)

            check_url