# -*- coding: utf-8 -*-
"""
    wakatime.arguments
    ~~~~~~~~~~~~~~~~~~

    Command-line arguments.

    :copyright: (c) 2016 Alan Hamlett.
    :license: BSD, see LICENSE for more details.
"""


from __future__ import print_function


import os
import re
import time
import traceback
from .__about__ import __version__
from .compat import basestring
from .configs import parseConfigFile
from .constants import AUTH_ERROR
from .packages import argparse


class FileAction(argparse.Action):

    def __call__(self, parser, namespace, values, option_string=None):
        if isinstance(values, basestring) and values.startswith('"'):
            values = re.sub(r'\\"', '"', values.strip('"'))
        try:
            if os.path.isfile(values):
                values = os.path.realpath(values)
        except:  # pragma: nocover
            pass
        setattr(namespace, self.dest, values)


class StoreWithoutQuotes(argparse.Action):

    def __call__(self, parser, namespace, values, option_string=None):
        if isinstance(values, basestring) and values.startswith('"'):
            values = re.sub(r'\\"', '"', values.strip('"'))
        setattr(namespace, self.dest, values)


def parse_arguments():
    """Parse command line arguments and configs from ~/.wakatime.cfg.
    Command line arguments take precedence over config file settings.
    Returns instances of ArgumentParser and SafeConfigParser.
    """

    # define supported command line arguments
    parser = argparse.ArgumentParser(
            description='Common interface for the WakaTime api.')
    parser.add_argument('--entity', dest='entity', metavar='FILE',
            action=FileAction,
            help='absolute path to file for the heartbeat; can also be a '+
                 'url, domain, or app when --entity-type is not file')
    parser.add_argument('--file', dest='file', action=FileAction,
            help=argparse.SUPPRESS)
    parser.add_argument('--key', dest='key', action=StoreWithoutQuotes,
            help='your wakatime api key; uses api_key from '+
                '~/.wakatime.cfg by default')
    parser.add_argument('--write', dest='is_write',
            action='store_true',
            help='when set, tells api this heartbeat was triggered from '+
                 'writing to a file')
    parser.add_argument('--plugin', dest='plugin', action=StoreWithoutQuotes,
            help='optional text editor plugin name and version '+
                 'for User-Agent header')
    parser.add_argument('--time', dest='timestamp', metavar='time',
            type=float, action=StoreWithoutQuotes,
            help='optional floating-point unix epoch timestamp; '+
                 'uses current time by default')
    parser.add_argument('--lineno', dest='lineno', action=StoreWithoutQuotes,
            help='optional line number; current line being edited')
    parser.add_argument('--cursorpos', dest='cursorpos', action=StoreWithoutQuotes,
            help='optional cursor position in the current file')
    parser.add_argument('--entity-type', dest='entity_type', action=StoreWithoutQuotes,
            help='entity type for this heartbeat. can be one of "file", '+
                 '"domain", or "app"; defaults to file.')
    parser.add_argument('--proxy', dest='proxy', action=StoreWithoutQuotes,
                        help='optional proxy configuration. Supports HTTPS '+
                        'and SOCKS proxies. For example: '+
                        'https://user:pass@host:port or '+
                        'socks5://user:pass@host:port or ' +
                        'domain\\user:pass')
    parser.add_argument('--no-ssl-verify', dest='nosslverify',
                        action='store_true',
                        help='disables SSL certificate verification for HTTPS '+
                        'requests. By default, SSL certificates are verified.')
    parser.add_argument('--project', dest='project', action=StoreWithoutQuotes,
            help='optional project name')
    parser.add_argument('--alternate-project', dest='alternate_project', action=StoreWithoutQuotes,
            help='optional alternate project name; auto-discovered project '+
                 'takes priority')
    parser.add_argument('--alternate-language', dest='alternate_language', action=StoreWithoutQuotes,
            help=argparse.SUPPRESS)
    parser.add_argument('--language', dest='language', action=StoreWithoutQuotes,
            help='optional language name; if valid, takes priority over '+
                 'auto-detected language')
    parser.add_argument('--hostname', dest='hostname', action=StoreWithoutQuotes, help='hostname of '+
                        'current machine.')
    parser.add_argument('--disable-offline', dest='offline',
            action='store_false',
            help='disables offline time logging instead of queuing logged time')
    parser.add_argument('--disableoffline', dest='offline_deprecated',
            action='store_true', help=argparse.SUPPRESS)
    parser.add_argument('--hide-filenames', dest='hide_filenames',
            action='store_true',
            help='obfuscate filenames; will not send file names to api')
    parser.add_argument('--hidefilenames', dest='hidefilenames',
            action='store_true',
            help=argparse.SUPPRESS)
    parser.add_argument('--exclude', dest='exclude', action='append',
            help='filename patterns to exclude from logging; POSIX regex '+
                 'syntax; can be used more than once')
    parser.add_argument('--include', dest='include', action='append',
            help='filename patterns to log; when used in combination with '+
                 '--exclude, files matching include will still be logged; '+
                 'POSIX regex syntax; can be used more than once')
    parser.add_argument('--include-only-with-project-file',
                        dest='include_only_with_project_file',
                        action='store_true',
                        help='disables tracking folders unless they contain '+
                        'a .wakatime-project file; defaults to false')
    parser.add_argument('--ignore', dest='ignore', action='append',
            help=argparse.SUPPRESS)
    parser.add_argument('--extra-heartbeats', dest='extra_heartbeats',
            action='store_true',
            help='reads extra heartbeats from STDIN as a JSON array until EOF')
    parser.add_argument('--log-file', dest='log_file', action=StoreWithoutQuotes,
            help='defaults to ~/.wakatime.log')
    parser.add_argument('--logfile', dest='logfile', action=StoreWithoutQuotes,
            help=argparse.SUPPRESS)
    parser.add_argument('--api-url', dest='api_url', action=StoreWithoutQuotes,
            help='heartbeats api url; for debugging with a local server')
    parser.add_argument('--apiurl', dest='apiurl', action=StoreWithoutQuotes,
            help=argparse.SUPPRESS)
    parser.add_argument('--timeout', dest='timeout', type=int, action=StoreWithoutQuotes,
            help='number of seconds to wait when sending heartbeats to api; '+
                 'defaults to 60 seconds')
    parser.add_argument('--config', dest='config', action=StoreWithoutQuotes,
            help='defaults to ~/.wakatime.cfg')
    parser.add_argument('--verbose', dest='verbose', action='store_true',
            help='turns on debug messages in log file')
    parser.add_argument('--version', action='version', version=__version__)

    # parse command line arguments
    args = parser.parse_args()

    # use current unix epoch timestamp by default
    if not args.timestamp:
        args.timestamp = time.time()

    # parse ~/.wakatime.cfg file
    configs = parseConfigFile(args.config)

    # update args from configs
    if not args.hostname:
        if configs.has_option('settings', 'hostname'):
            args.hostname = configs.get('settings', 'hostname')
    if not args.key:
        default_key = None
        if configs.has_option('settings', 'api_key'):
            default_key = configs.get('settings', 'api_key')
        elif configs.has_option('settings', 'apikey'):
            default_key = configs.get('settings', 'apikey')
        if default_key:
            args.key = default_key
        else:
            try:
                parser.error('Missing api key. Find your api key from wakatime.com/settings.')
            except SystemExit:
                raise SystemExit(AUTH_ERROR)

    is_valid = not not re.match(r'^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$', args.key, re.I)
    if not is_valid:
        try:
            parser.error('Invalid api key. Find your api key from wakatime.com/settings.')
        except SystemExit:
            raise SystemExit(AUTH_ERROR)

    if not args.entity:
        if args.file:
            args.entity = args.file
        else:
            parser.error('argument --entity is required')

    if not args.language and args.alternate_language:
        args.language = args.alternate_language

    if not args.exclude:
        args.exclude = []
    if configs.has_option('settings', 'ignore'):
        try:
            for pattern in configs.get('settings', 'ignore').split("\n"):
                if pattern.strip() != '':
                    args.exclude.append(pattern)
        except TypeError:  # pragma: nocover
            pass
    if configs.has_option('settings', 'exclude'):
        try:
            for pattern in configs.get('settings', 'exclude').split("\n"):
                if pattern.strip() != '':
                    args.exclude.append(pattern)
        except TypeError:  # pragma: nocover
            pass
    if not args.include_only_with_project_file and configs.has_option('settings', 'include_only_with_project_file'):
        args.include_only_with_project_file = configs.get('settings', 'include_only_with_project_file')
    if not args.include:
        args.include = []
    if configs.has_option('settings', 'include'):
        try:
            for pattern in configs.get('settings', 'include').split("\n"):
                if pattern.strip() != '':
                    args.include.append(pattern)
        except TypeError:  # pragma: nocover
            pass
    if not args.hide_filenames and args.hidefilenames:
        args.hide_filenames = args.hidefilenames
    if args.hide_filenames:
        args.hide_filenames = ['.*']
    else:
        args.hide_filenames = []
        option = None
        if configs.has_option('settings', 'hidefilenames'):
            option = configs.get('settings', 'hidefilenames')
        if configs.has_option('settings', 'hide_filenames'):
            option = configs.get('settings', 'hide_filenames')
        if option is not None:
            if option.strip().lower() == 'true':
                args.hide_filenames = ['.*']
            elif option.strip().lower() != 'false':
                for pattern in option.split("\n"):
                    if pattern.strip() != '':
                        args.hide_filenames.append(pattern)
    if args.offline_deprecated:
        args.offline = False
    if args.offline and configs.has_option('settings', 'offline'):
        args.offline = configs.getboolean('settings', 'offline')
    if not args.proxy and configs.has_option('settings', 'proxy'):
        args.proxy = configs.get('settings', 'proxy')
    if args.proxy:
        pattern = r'^((https?|socks5)://)?([^:@]+(:([^:@])+)?@)?[^:]+(:\d+)?$'
        if '\\' in args.proxy:
            pattern = r'^.*\\.+$'
        is_valid = not not re.match(pattern, args.proxy, re.I)
        if not is_valid:
            parser.error('Invalid proxy. Must be in format ' +
                         'https://user:pass@host:port or ' +
                         'socks5://user:pass@host:port or ' +
                         'domain\\user:pass.')
    if configs.has_option('settings', 'no_ssl_verify'):
        args.nosslverify = configs.getboolean('settings', 'no_ssl_verify')
    if not args.verbose and configs.has_option('settings', 'verbose'):
        args.verbose = configs.getboolean('settings', 'verbose')
    if not args.verbose and configs.has_option('settings', 'debug'):
        args.verbose = configs.getboolean('settings', 'debug')
    if not args.log_file and args.logfile:
        args.log_file = args.logfile
    if not args.log_file and configs.has_option('settings', 'log_file'):
        args.log_file = configs.get('settings', 'log_file')
    if not args.log_file and os.environ.get('WAKATIME_HOME'):
        home = os.environ.get('WAKATIME_HOME')
        args.log_file = os.path.join(os.path.expanduser(home), '.wakatime.log')
    if not args.api_url and args.apiurl:
        args.api_url = args.apiurl
    if not args.api_url and configs.has_option('settings', 'api_url'):
        args.api_url = configs.get('settings', 'api_url')
    if not args.timeout and configs.has_option('settings', 'timeout'):
        try:
            args.timeout = int(configs.get('settings', 'timeout'))
        except ValueError:
            print(traceback.format_exc())

    return args, configs
