# Copyright 2019 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""

Helper file for handling policy fetching/parsing/formatting. This file has
working unitests located in ${THIS_FILE_NAME}_unittest.py. If you modify this
file, add/fix/adjust the unittests for proper code coverage and
re-run.

Read instructions in the unittest file on how to run.

"""
import json
import time

import six

from autotest_lib.client.common_lib import error

# Default settings for managed user policies


def get_all_policies(autotest_ext):
    """Returns a dict of all the policies on the device."""

    policy_data = _get_pol_from_api(autotest_ext)
    if not policy_data:
        raise error.TestError('API did not return policy data!')
    _reformat_policies(policy_data)

    return policy_data


def _get_pol_from_api(autotest_ext):
    """Call the getAllPolicies API, return raw result, clear stored data."""
    new_promise = '''new Promise(function(resolve, reject) {
        chrome.autotestPrivate.getAllEnterprisePolicies(function(policies) {
          if (chrome.runtime.lastError) {
            reject(new Error(chrome.runtime.lastError.message));
          } else {
            resolve(policies);
          }
        })
    })'''
    policy_data = autotest_ext.EvaluateJavaScript(new_promise, promise=True)
    return policy_data


def _wait_for_new_pols_after_refresh(autotest_ext):
    """
    Wait up to 1 second for the polices to update when refreshing.

    Note: This is non-breaking, thus even if the policies do not update, once
    the timeout expires, the function will exit.

    """
    prior = _get_pol_from_api(autotest_ext)
    _call_refresh_policies_from_api(autotest_ext)
    t1 = time.time()
    while time.time() - t1 < 1:
        curr = _get_pol_from_api(autotest_ext)
        if curr != prior:
            break


def _call_refresh_policies_from_api(autotest_ext):
    """Call the refreshEnterprisePolicies from autotestPrivate."""
    new_promise = '''new Promise(function(resolve, reject) {
        chrome.autotestPrivate.refreshEnterprisePolicies(function() {
          if (chrome.runtime.lastError) {
            reject(new Error(chrome.runtime.lastError.message));
          } else {
            resolve();
          }
        })
    })'''
    autotest_ext.EvaluateJavaScript(new_promise, promise=True)


def refresh_policies(autotest_ext, wait_for_new=False):
    """Force a policy fetch."""

    if wait_for_new:
        _wait_for_new_pols_after_refresh(autotest_ext)
    else:
        _call_refresh_policies_from_api(autotest_ext)


def _reformat_policies(policy_dict):
    """
    Reformat visually formatted dicts to type dict (and not unicode).

    Given a dict, check if 'value' is a key in the value field. If so, check
    if the data of ['value'] is unicode. If it is, attempt to load it as json.
    If not, but the value field is a dict, recursively call this function to
    check again.

    @param: policy_dict, a policy dictionary.

    """
    for k, v in policy_dict.items():
        if not v:
            # No data
            continue
        if 'value' in v:
            if type(v['value']) == six.text_type:
                _remove_visual_formatting(v)
        elif isinstance(v, dict):
            _reformat_policies(v)


def _remove_visual_formatting(policy_value):
    """
    Attempt to remove any visual formatting.

    Note: policy_value can be either unicode dict or string. If json.loads()
    raises a ValueError, it is a string, and we do not need to format.

    """
    try:
        policy_value['value'] = json.loads(policy_value['value'])
    except ValueError:
        return
