#!/usr/bin/env python

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.

"""
Creates and/or modifies a Firefox profile.
The profile can be modified by passing in addons to install or preferences to set.
If no profile is specified, a new profile is created and the path of the
resulting profile is printed.
"""
from __future__ import absolute_import, print_function

import sys
from optparse import OptionParser
from .prefs import Preferences
from .profile import FirefoxProfile
from .profile import Profile

__all__ = ['MozProfileCLI', 'cli', 'KeyValueParseError', 'parse_key_value', 'parse_preferences']


class KeyValueParseError(Exception):
    """Error when parsing strings of serialized key-values."""

    def __init__(self, msg, errors=()):
        self.errors = errors
        Exception.__init__(self, msg)


def parse_key_value(strings, separator='=', context='key, value'):
    """Parse string-serialized key-value pairs in the form of `key = value`.

    Args:
        strings (list): List of strings to parse.
        separator (str): Identifier used to split the strings.

    Returns:
        list: A list of (<key>, <value>) tuples. Whitespace is not stripped.

    Raises:
        KeyValueParseError
    """

    # syntax check
    missing = [string for string in strings if separator not in string]
    if missing:
        raise KeyValueParseError(
            "Error: syntax error in %s: %s" %
            (context, ','.join(missing)), errors=missing)
    return [string.split(separator, 1) for string in strings]


def parse_preferences(prefs, context='--setpref='):
    """Parse preferences specified on the command line.

    Args:
        prefs (list): A list of strings, usually of the form "<pref>=<value>".

    Returns:
        dict: A dictionary of the form {<pref>: <value>} where values have been
              cast.
    """
    try:
        prefs = dict(parse_key_value(prefs, context=context))
    except KeyValueParseError as e:
        print(str(e))
        sys.exit(1)

    return {k: Preferences.cast(v) for k, v in prefs.items()}


class MozProfileCLI(object):
    """The Command Line Interface for ``mozprofile``."""

    module = 'mozprofile'
    profile_class = Profile

    def __init__(self, args=sys.argv[1:], add_options=None):
        self.parser = OptionParser(description=__doc__)
        self.add_options(self.parser)
        if add_options:
            add_options(self.parser)
        (self.options, self.args) = self.parser.parse_args(args)

    def add_options(self, parser):

        parser.add_option("-p", "--profile", dest="profile",
                          help="The path to the profile to operate on. "
                          "If none, creates a new profile in temp directory")
        parser.add_option("-a", "--addon", dest="addons",
                          action="append", default=[],
                          help="Addon paths to install. Can be a filepath, "
                          "a directory containing addons, or a url")
        parser.add_option("--pref", dest="prefs",
                          action='append', default=[],
                          help="A preference to set. "
                          "Must be a key-value pair separated by a ':'")
        parser.add_option("--preferences", dest="prefs_files",
                          action='append', default=[],
                          metavar="FILE",
                          help="read preferences from a JSON or INI file. "
                          "For INI, use 'file.ini:section' to specify a particular section.")

    def profile_args(self):
        """arguments to instantiate the profile class"""
        return dict(profile=self.options.profile,
                    addons=self.options.addons,
                    preferences=self.preferences())

    def preferences(self):
        """profile preferences"""

        # object to hold preferences
        prefs = Preferences()

        # add preferences files
        for prefs_file in self.options.prefs_files:
            prefs.add_file(prefs_file)

        # change CLI preferences into 2-tuples
        cli_prefs = parse_key_value(self.options.prefs, separator=':')

        # string preferences
        prefs.add(cli_prefs, cast=True)

        return prefs()

    def profile(self, restore=False):
        """create the profile"""

        kwargs = self.profile_args()
        kwargs['restore'] = restore
        return self.profile_class(**kwargs)


def cli(args=sys.argv[1:]):
    """ Handles the command line arguments for ``mozprofile`` via ``sys.argv``"""

    # add a view method for this cli method only
    def add_options(parser):
        parser.add_option('--view', dest='view',
                          action='store_true', default=False,
                          help="view summary of profile following invocation")
        parser.add_option('--firefox', dest='firefox_profile',
                          action='store_true', default=False,
                          help="use FirefoxProfile defaults")

    # process the command line
    cli = MozProfileCLI(args, add_options)

    if cli.args:
        cli.parser.error("Program doesn't support positional arguments.")

    if cli.options.firefox_profile:
        cli.profile_class = FirefoxProfile

    # create the profile
    profile = cli.profile()

    if cli.options.view:
        # view the profile, if specified
        print(profile.summary())
        return

    # if no profile was passed in print the newly created profile
    if not cli.options.profile:
        print(profile.profile)


if __name__ == '__main__':
    cli()
