import json
import os

import parallels.plesk.source.custom
from parallels.core.utils.common import open_no_inherit
from parallels.core.utils.migrator_utils import get_package_root_path
from parallels.plesk.hosting_description.validate.validators.bool import ValidatorBool
from parallels.plesk.hosting_description.validate.validators.dict import ValidatorDict
from parallels.plesk.hosting_description.validate.validators.list import ValidatorList
from parallels.plesk.hosting_description.validate.validators.selector import ValidatorSelector
from parallels.plesk.hosting_description.validate.validators.string import ValidatorString
from parallels.plesk.hosting_description.validate.validators.struct import ValidatorStruct


def create_hosting_description_validator():
    """Create validator for hosting descriptions file (from schema.json)

    :rtype: parallels.plesk.hosting_description.validate.validators.base.ValidatorBase
    """
    schema_filename = os.path.join(get_package_root_path(parallels.plesk.hosting_description), 'schema.json')
    with open_no_inherit(schema_filename) as fp:
        return create_validator_from_json_schema(json.load(fp))


def create_validator_from_json_schema(schema):
    """Create validator for specified JSON schema

    :rtype: parallels.plesk.hosting_description.validate.validators.base.ValidatorBase
    """
    root_element = schema["root_element"]
    return _create_validator_element(root_element, schema['elements'])


def _create_validator_element(element, elements_description):
    """Create validator for specified schema element

    :param element: Element to create validator for
    :param elements_description: Description of all elements in the schema
    :rtype: parallels.plesk.hosting_description.validate.validators.base.ValidatorBase
    """
    if element['type'] == 'string':
        return ValidatorString()
    if element['type'] == 'bool':
        return ValidatorBool()
    elif element['type'] == 'struct':
        required_items = {k for k, v in element['keys'].items() if v.get('required')}
        return ValidatorStruct(
            {k: _create_validator_element(v, elements_description) for k, v in element['keys'].items()},
            required_items,
            element.get('context_title'),
            element.get('context_var')
        )
    elif element['type'] == 'dict':
        return ValidatorDict(
            _create_validator_element(element['elements']['key'], elements_description),
            _create_validator_element(element['elements']['value'], elements_description),
        )
    elif element['type'] == 'list':
        return ValidatorList(_create_validator_element(element['elements'], elements_description))
    elif element['type'] == 'selector':
        return ValidatorSelector([_create_validator_element(e, elements_description) for e in element['types']])
    else:
        return _create_validator_element(elements_description[element['type']], elements_description)
