from importlib.resources import files as _resource_files

from xmlschema import XMLSchema as _XMLSchema
from xmlschema.exceptions import XMLSchemaException as _XMLSchemaException

import saml2.data.schemas as _data_schemas


class XMLSchemaError(Exception):
    """Generic error raised when the schema does not validate with a document"""


def _create_xml_schema_validator(source=None, **kwargs):
    schema_resources = _resource_files(_data_schemas)
    path_schema_xml = str(schema_resources.joinpath("xml.xsd"))
    path_schema_envelope = str(schema_resources.joinpath("envelope.xsd"))
    path_schema_xenc = str(schema_resources.joinpath("xenc-schema.xsd"))
    path_schema_xenc_11 = str(schema_resources.joinpath("xenc-schema-11.xsd"))
    path_schema_xmldsig_core = str(schema_resources.joinpath("xmldsig-core-schema.xsd"))
    path_schema_saml_assertion = str(schema_resources.joinpath("saml-schema-assertion-2.0.xsd"))
    path_schema_saml_metadata = str(schema_resources.joinpath("saml-schema-metadata-2.0.xsd"))
    path_schema_saml_protocol = str(schema_resources.joinpath("saml-schema-protocol-2.0.xsd"))
    path_schema_eidas_metadata_servicelist = str(schema_resources.joinpath("eidas-schema-metadata-servicelist.xsd"))
    path_schema_eidas_saml_extensions = str(schema_resources.joinpath("eidas-schema-saml-extensions.xsd"))
    path_schema_eidas_attribute_naturalperson = str(
        schema_resources.joinpath("eidas-schema-attribute-naturalperson.xsd")
    )
    path_schema_eidas_attribute_legalperson = str(schema_resources.joinpath("eidas-schema-attribute-legalperson.xsd"))

    source = source if source else path_schema_saml_protocol
    locations = {
        "http://www.w3.org/XML/1998/namespace": path_schema_xml,
        "http://schemas.xmlsoap.org/soap/envelope/": path_schema_envelope,
        "http://www.w3.org/2001/04/xmlenc#": path_schema_xenc,
        "http://www.w3.org/2009/xmlenc11#": path_schema_xenc_11,
        "http://www.w3.org/2000/09/xmldsig#": path_schema_xmldsig_core,
        "urn:oasis:names:tc:SAML:2.0:assertion": path_schema_saml_assertion,
        "urn:oasis:names:tc:SAML:2.0:metadata": path_schema_saml_metadata,
        "urn:oasis:names:tc:SAML:2.0:protocol": path_schema_saml_protocol,
        "http://eidas.europa.eu/metadata/servicelist": path_schema_eidas_metadata_servicelist,
        "http://eidas.europa.eu/saml-extensions": path_schema_eidas_saml_extensions,
        "http://eidas.europa.eu/attributes/naturalperson": path_schema_eidas_attribute_naturalperson,
        "http://eidas.europa.eu/attributes/legalperson": path_schema_eidas_attribute_legalperson,
    }

    kwargs = {
        **kwargs,
        "validation": "strict",
        "locations": locations,
        "base_url": source,
        "allow": "sandbox",
        "use_fallback": False,
    }
    return _XMLSchema(source, **kwargs)


_schema_validator_default = _create_xml_schema_validator()


def validate(doc, validator=None):
    validator = _schema_validator_default if validator is None else validator
    try:
        validator.validate(doc)
    except _XMLSchemaException as e:
        error_context = {
            "doc": doc,
            "error": str(e),
        }
        raise XMLSchemaError(error_context) from e
    except Exception as e:
        error_context = {
            "doc": doc,
            "error": str(e),
        }
        raise XMLSchemaError(error_context) from e
