#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Copyright (C) 2017-2018 Robin Schneider <ypid@riseup.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, version 3 of the
# License.
#
# This program 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

"""
Check if the proxy specified as Acquire::HTTP::Proxy or Acquire::HTTPS::Proxy
are currently reachable before promoting them to be used by the currently
running apt process. Without this script, apt will fail when the proxy can not
be reached.  With this script, apt silently falls back to "DIRECT" (no proxy)
if the proxy is (temporally) not reachable.

To use this script, set:

    Acquire::HTTP::Proxy-Auto-Detect "/usr/local/lib/get-reachable-apt-proxy";

in your APT configuration.
"""

from __future__ import (print_function, unicode_literals,
                        absolute_import, division)

import argparse
import logging

#  from future.standard_library import install_aliases
#  install_aliases()

from urllib.parse import urlparse
from urllib.request import urlopen
from urllib.error import URLError, HTTPError

import apt_pkg

__license__ = 'AGPL-3.0-only'
__author__ = 'Robin Schneider <ypid@riseup.net>'
__version__ = '0.1.0'

LOG = logging.getLogger(__name__)


def get_first_up_proxy(target_uri_raw):
    possible_proxies = []

    LOG.debug("Trying to find proxy for URI: {}".format(target_uri_raw))

    target_uri = urlparse(target_uri_raw)

    apt_config_key = "Acquire::{}::Proxy".format(
        target_uri.scheme.upper()
    )

    if apt_pkg.config.exists(apt_config_key):
        proxy_uris = apt_pkg.config.get(apt_config_key)
        for proxy_uri_raw in proxy_uris.split(' '):

            if proxy_uri_raw.upper() in ['DIRECT']:
                LOG.debug("Skipping special proxy keyword: {}".format(
                    proxy_uri_raw))
                continue
            else:
                LOG.debug("Checking possible proxy URI: {}".format(
                    proxy_uri_raw))

            proxy_uri = urlparse(proxy_uri_raw)

            if proxy_uri.scheme in ['https', 'http']:
                try:
                    urlopen(proxy_uri_raw, timeout=1)
                except HTTPError as err:
                    LOG.debug("Proxy responded with: {}".format(err))
                    pass
                except URLError as err:
                    LOG.debug("Proxy not suitable: {}".format(err))
                    continue

                LOG.info("Adding proxy to list of possible proxies: {}".format(
                    proxy_uri_raw))
                possible_proxies.append(proxy_uri_raw)
                break
            else:
                raise NotImplementedError(
                    "URI scheme {} is not yet implemented.".format())

    possible_proxies.append('DIRECT')

    return possible_proxies[0]


def get_args_parser():
    args_parser = argparse.ArgumentParser(
        prog='get-reachable-apt-proxy',
        epilog=__doc__,
    )
    args_parser.add_argument(
        '-V', '--version',
        action='version',
        version=__version__,
    )
    args_parser.add_argument(
        '-d', '--debug',
        help="Write debugging and higher to STDOUT|STDERR.",
        action="store_const",
        dest="loglevel",
        const=logging.DEBUG,
    )
    args_parser.add_argument(
        '-v', '--verbose',
        help="Write information and higher to STDOUT|STDERR.",
        action="store_const",
        dest="loglevel",
        const=logging.INFO,
    )
    args_parser.add_argument(
        'uri',
    )

    return args_parser


def main():
    args_parser = get_args_parser()
    args_parser.set_defaults(loglevel=logging.WARN)
    args = args_parser.parse_args()

    logging.basicConfig(
        format='%(levelname)s{}, %(asctime)s: %(message)s'.format(
            ' (%(filename)s:%(lineno)s)'
            if args.loglevel <= logging.DEBUG else '',
        ),
        level=args.loglevel,
    )

    apt_pkg.init_config()
    print(get_first_up_proxy(args.uri))


if __name__ == '__main__':
    main()
