import requests
import uuid

import bottle


class LastModifiedContainer:

    def __init__(self):
        self.lm = None
        self.lm_epoch = 0

    @property
    def last_modified(self):
        return self.lm_epoch

    @property
    def last_mod_header(self):
        return self.lm

    @last_mod_header.setter
    def last_mod_header(self, lm):
        self.lm = lm
        self.lm_epoch = bottle.parse_date(lm) or 0


class VersionManager(object):

    class Version:
        def __init__(self, last_mod: LastModifiedContainer):
            self.last_mod = last_mod

        @property
        def last_modified(self):
            return self.last_mod.last_modified

    class NullVersion(Version):
        def __init__(self, last_mod):
            super().__init__(last_mod)
            self.version = None

    class TorVersion(Version):

        def __init__(self, last_mod):
            super().__init__(last_mod)
            self.stable = None
            self.unstable = None

        def is_latest_stable(self, version):
            if self.stable is None:
                return None
            return str(version) == self.stable

        def is_latest_unstable(self, version):
            if self.unstable is None:
                return None
            return str(version) == self.unstable
        
    class BoxVersion(Version):

        def __init__(self, last_mod):
            super().__init__(last_mod)
            self.version = None
            self.message = None

        def latest_version(self):
            if self.version is None:
                return None

            latest = '{}.{}'.format(self.version[0], self.version[1])
            if self.version[2] != 0:
                latest += '.{}'.format(self.version[2])
            return latest

        def is_latest_tag(self, tag):

            if self.version is None:
                return None

            t2v = self.tag2version(tag)

            if self.tag_is_RC(tag) is True:
                # if tag is an RC, a non RC version with same number is the latest one!
                return t2v > self.version
            else:
                return t2v >= self.version

        @staticmethod
        # returns a version tuple
        def tag2version(tag):

            if tag[0] == 'v':
                tag = tag[1:]

            tag = tag.lower().split('rc')[0]    # strip 'RC..'
            tag = tag.lower().split('dev')[0]   # strip 'dev'
            tag = tag.lower().split('post')[0]   # strip 'post'
            tag = tag.lower().split('alpha')[0]   # strip 'alpha'

            if tag[-1:] == '.':
                tag = tag[:-1]

            v = tag.split('.')
            v = [int(y) for y in v]  # string to int
            while len(v) < 3:
                v.append(0)

            return tuple(v[:3])

        @staticmethod
        # check if tag has 'rc' in it
        def tag_is_RC(tag):
            tags = tag.lower().split('rc')
            return len(tags) > 1

    def __init__(self, proxy, current_version, system, release):
        self.id = uuid.uuid4().hex
        self.proxy = proxy
        self.lmc = LastModifiedContainer()
        self.Tor = self.TorVersion(self.lmc)
        self.Box = self.BoxVersion(self.lmc)
        self.Null = self.NullVersion(self.lmc)
        self.version = current_version
        self.system = system
        self.release = release
        self.last_mod = ''

    def update(self):

        # No service without proxy!
        if self.proxy is None:
            return False

        proxy_address = self.proxy.address()
        if proxy_address is None:
            return False

        # v19.2
        __VERSION_PROTOCOL__ = 3

        proxies = {
            'http': 'socks5h://' + proxy_address,
            'https': 'socks5h://' + proxy_address
        }
        payload = {
            'protocol': __VERSION_PROTOCOL__,
            'version': self.version,
            'system': self.system,
            'release': self.release
        }

        headers = {
            'if-modified-since': self.lmc.last_mod_header
        }

        address = 'http://t527moy64zwxsfhb.onion/{}/check.html'.format(self.id)

        r = None

        try:
            r = requests.get(address, proxies=proxies, headers=headers, params=payload, timeout=10)
        except Exception as exc:
            pass

        if r is None:
            return False
        if r.status_code == requests.codes.not_modified:
            return True
        if r.status_code != 200:
            return False

        back = r.json()

        if 'protocol' not in back:
            return False

        if int(back['protocol']) != __VERSION_PROTOCOL__:
            return False

        # 'protocol': 3
        # 'tor':
        #   'stable':   latest stable tor version
        #   'unstable': latest unstable (alpha, rc) tor version
        # 'box':
        #   'latest': [a, b, c] version number of latest release
        #   'message': Message returned from the Update Service
        # 'null':
        #   'version': Latest version of the tornull definition file

        if 'tor' in back:
            _tor = back['tor']
            if 'stable' in _tor:
                self.Tor.stable = _tor['stable']
            if 'unstable' in _tor:
                self.Tor.unstable = _tor['unstable']

        if 'box' in back:
            _box = back['box']
            if 'latest' in _box:
                latest = _box['latest']
                try:
                    while len(latest) < 3:
                        latest.append(0)
                    self.Box.version = tuple(latest[:3])
                except:
                    pass
            if 'message' in _box:
                self.Box.message = _box['message']

        _null = back.get('null', None)
        if _null is not None:
            self.Null.version = _null.get('version', None)

        self.lmc.last_mod_header = r.headers.get('Last-Modified', '')

        return True
