# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010-2014 Nexedi SA and Contributors.
# All Rights Reserved.
#
# This program is free software: you can Use, Study, Modify and Redistribute
# it under the terms of the GNU General Public License version 3, or (at your
# option) any later version, as published by the Free Software Foundation.
#
# You can also Link and Combine this program with other software covered by
# the terms of any of the Free Software licenses or any of the Open Source
# Initiative approved licenses and Convey the resulting work. Corresponding
# source of such a combination shall include the source code for all other
# software used.
#
# This program is distributed WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See COPYING file for full licensing terms.
# See https://www.nexedi.com/licensing for rationale and options.
#
##############################################################################

import ast
import hashlib
import json
import re
import requests
import sys

import prettytable

from slapos.grid import networkcache
from slapos.grid.distribution import distribution_tuple
from slapos.cli.config import ConfigCommand
from slapos.util import str2bytes

FAILURE_EXIT_CODE = 10

class CacheLookupCommand(ConfigCommand):
    """
    perform a query to the networkcache
    You can provide either a complete URL to the software release,
    or a corresponding MD5 hash value.

    The command will report which OS distribution/version have a binary
    cache of the software release, and which ones are compatible
    with the OS you are currently running.
    """

    def get_parser(self, prog_name):
        ap = super(CacheLookupCommand, self).get_parser(prog_name)
        ap.add_argument('software_url',
                        help='Your software url or MD5 hash')
        return ap

    def take_action(self, args):
        configp = self.fetch_config(args)
        cache_dir = configp.get('networkcache', 'download-binary-dir-url')
        sys.exit(
          do_lookup(self.app.log, cache_dir, args.software_url))

def looks_like_md5(s):
    """
    Return True if the parameter looks like an hashed value.
    Not 100% precise, but we're actually more interested in filtering out URLs and pathnames.
    """
    return re.match('[0-9a-f]{32}', s)


def do_lookup(logger, cache_dir, software_url):
    if looks_like_md5(software_url):
        md5 = software_url
    else:
        md5 = hashlib.md5(str2bytes(software_url)).hexdigest()
    try:
        url = '%s/%s' % (cache_dir, md5)
        logger.debug('Connecting to %s', url)
        req = requests.get(url, timeout=5)
    except (requests.Timeout, requests.ConnectionError):
        logger.critical('Cannot connect to cache server at %s', url)
        return FAILURE_EXIT_CODE

    if not req.ok:
        if req.status_code == 404:
            logger.critical('Object not in cache: %s', software_url)
        else:
            logger.critical('Error while looking object %s: %s', software_url, req.reason)
        return FAILURE_EXIT_CODE

    entries = req.json()

    if not entries:
        logger.info('Object found in cache, but has no binary entries.')
        return 0

    ostable = sorted(ast.literal_eval(json.loads(entry[0])['os']) for entry in entries)

    pt = prettytable.PrettyTable(['distribution', 'version', 'id', 'compatible?'])

    linux_distribution = distribution_tuple()

    for os in ostable:
        compatible = 'yes' if networkcache.os_matches(os, linux_distribution) else 'no'
        pt.add_row([os[0], os[1], os[2], compatible])

    meta = json.loads(entries[0][0])
    logger.info('Software URL: %s', meta['software_url'])
    logger.info('MD5:          %s', md5)

    for line in pt.get_string(border=True, padding_width=0, vrules=prettytable.NONE).split('\n'):
        logger.info(line)

    return 0
