import pytest
from lxml import etree

from tests.utils import DummyTransport, assert_nodes_equal, load_xml, render_node
from zeep import exceptions, xsd
from zeep.xsd import Schema
from zeep.xsd.types.unresolved import UnresolvedType


def test_default_types():
    schema = xsd.Schema()
    xsd_string = schema.get_type("{http://www.w3.org/2001/XMLSchema}string")
    assert xsd_string == xsd.String()


def test_default_types_not_found():
    schema = xsd.Schema()
    with pytest.raises(exceptions.LookupError):
        schema.get_type("{http://www.w3.org/2001/XMLSchema}bar")


def test_default_elements():
    schema = xsd.Schema()
    xsd_schema = schema.get_element("{http://www.w3.org/2001/XMLSchema}schema")
    isinstance(xsd_schema, Schema)


def test_default_elements_not_found():
    schema = xsd.Schema()
    with pytest.raises(exceptions.LookupError):
        schema.get_element("{http://www.w3.org/2001/XMLSchema}bar")


def test_invalid_namespace_handling():
    schema = xsd.Schema()
    qname = "{http://tests.python-zeep.org/404}foo"

    with pytest.raises(exceptions.NamespaceError) as exc:
        schema.get_element(qname)
    assert qname in str(exc.value.message)

    with pytest.raises(exceptions.NamespaceError) as exc:
        schema.get_type(qname)
    assert qname in str(exc.value.message)

    with pytest.raises(exceptions.NamespaceError) as exc:
        schema.get_group(qname)
    assert qname in str(exc.value.message)

    with pytest.raises(exceptions.NamespaceError) as exc:
        schema.get_attribute(qname)
    assert qname in str(exc.value.message)

    with pytest.raises(exceptions.NamespaceError) as exc:
        schema.get_attribute_group(qname)
    assert qname in str(exc.value.message)


def test_invalid_localname_handling():
    schema = xsd.Schema(
        load_xml(
            """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/"
            targetNamespace="http://tests.python-zeep.org/"
            elementFormDefault="qualified">
        </xs:schema>
    """
        )
    )

    qname = "{http://tests.python-zeep.org/}foo"
    namespace = "http://tests.python-zeep.org/"
    localname = "foo"

    with pytest.raises(exceptions.LookupError) as exc:
        schema.get_element(qname)
    assert namespace in str(exc.value.message)
    assert localname in str(exc.value.message)

    with pytest.raises(exceptions.LookupError) as exc:
        schema.get_type(qname)
    assert namespace in str(exc.value.message)
    assert localname in str(exc.value.message)

    with pytest.raises(exceptions.LookupError) as exc:
        schema.get_group(qname)
    assert namespace in str(exc.value.message)
    assert localname in str(exc.value.message)

    with pytest.raises(exceptions.LookupError) as exc:
        schema.get_attribute(qname)
    assert namespace in str(exc.value.message)
    assert localname in str(exc.value.message)

    with pytest.raises(exceptions.LookupError) as exc:
        schema.get_attribute_group(qname)
    assert namespace in str(exc.value.message)
    assert localname in str(exc.value.message)


def test_schema_repr_none():
    schema = xsd.Schema()
    assert repr(schema) == "<Schema()>"


def test_schema_repr_val():
    schema = xsd.Schema(
        load_xml(
            """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/"
            targetNamespace="http://tests.python-zeep.org/"
            elementFormDefault="qualified">
        </xs:schema>
    """
        )
    )
    assert (
        repr(schema) == "<Schema(location=None, tns='http://tests.python-zeep.org/')>"
    )


def test_schema_doc_repr_val():
    schema = xsd.Schema(
        load_xml(
            """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/"
            targetNamespace="http://tests.python-zeep.org/"
            elementFormDefault="qualified">
        </xs:schema>
    """
        )
    )
    docs = schema._get_schema_documents("http://tests.python-zeep.org/")
    assert len(docs) == 1
    doc = docs[0]
    assert (
        repr(doc)
        == "<SchemaDocument(location=None, tns='http://tests.python-zeep.org/', is_empty=True)>"
    )


def test_multiple_extension():
    node_a = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/a"
            targetNamespace="http://tests.python-zeep.org/a"
            xmlns:b="http://tests.python-zeep.org/b"
            elementFormDefault="qualified">

            <xs:import
                schemaLocation="http://tests.python-zeep.org/b.xsd"
                namespace="http://tests.python-zeep.org/b"/>

            <xs:complexType name="type_a">
              <xs:complexContent>
                <xs:extension base="b:type_b"/>
              </xs:complexContent>
            </xs:complexType>
            <xs:element name="typetje" type="tns:type_a"/>

        </xs:schema>
    """.strip()
    )

    node_b = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/b"
            targetNamespace="http://tests.python-zeep.org/b"
            xmlns:c="http://tests.python-zeep.org/c"
            elementFormDefault="qualified">

            <xs:import
                schemaLocation="http://tests.python-zeep.org/c.xsd"
                namespace="http://tests.python-zeep.org/c"/>

            <xs:complexType name="type_b">
              <xs:complexContent>
                <xs:extension base="c:type_c"/>
              </xs:complexContent>
            </xs:complexType>
        </xs:schema>
    """.strip()
    )

    node_c = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/c"
            targetNamespace="http://tests.python-zeep.org/c"
            elementFormDefault="qualified">

            <xs:complexType name="type_c">
              <xs:complexContent>
                <xs:extension base="tns:type_d"/>
              </xs:complexContent>
            </xs:complexType>

            <xs:complexType name="type_d">
                <xs:attribute name="wat" type="xs:string" />
            </xs:complexType>
        </xs:schema>
    """.strip()
    )
    etree.XMLSchema(node_c)

    transport = DummyTransport()
    transport.bind("http://tests.python-zeep.org/b.xsd", node_b)
    transport.bind("http://tests.python-zeep.org/c.xsd", node_c)

    schema = xsd.Schema(node_a, transport=transport)
    type_a = schema.get_type("ns0:type_a")
    type_a(wat="x")


def test_global_element_and_type():
    node_a = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/a"
            targetNamespace="http://tests.python-zeep.org/a"
            xmlns:b="http://tests.python-zeep.org/b"
            elementFormDefault="qualified">

            <xs:import
                schemaLocation="http://tests.python-zeep.org/b.xsd"
                namespace="http://tests.python-zeep.org/b"/>

            <xs:complexType name="refs">
              <xs:sequence>
                <xs:element ref="b:ref_elm"/>
              </xs:sequence>
              <xs:attribute ref="b:ref_attr"/>
            </xs:complexType>

        </xs:schema>
    """.strip()
    )

    node_b = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/b"
            targetNamespace="http://tests.python-zeep.org/b"
            xmlns:c="http://tests.python-zeep.org/c"
            elementFormDefault="qualified">

            <xs:import
                schemaLocation="http://tests.python-zeep.org/c.xsd"
                namespace="http://tests.python-zeep.org/c"/>

            <xs:element name="ref_elm" type="xs:string"/>
            <xs:attribute name="ref_attr" type="xs:string"/>
        </xs:schema>
    """.strip()
    )

    node_c = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/c"
            targetNamespace="http://tests.python-zeep.org/c"
            elementFormDefault="qualified">

            <xs:complexType name="type_a">
              <xs:sequence>
                <xs:element name="item_a" type="xs:string"/>
              </xs:sequence>
            </xs:complexType>
            <xs:element name="item" type="xs:string"/>
        </xs:schema>
    """.strip()
    )
    etree.XMLSchema(node_c)

    transport = DummyTransport()
    transport.bind("http://tests.python-zeep.org/b.xsd", node_b)
    transport.bind("http://tests.python-zeep.org/c.xsd", node_c)

    schema = xsd.Schema(node_a, transport=transport)
    type_a = schema.get_type("{http://tests.python-zeep.org/c}type_a")

    type_a = schema.get_type("{http://tests.python-zeep.org/c}type_a")
    type_a(item_a="x")

    elm = schema.get_element("{http://tests.python-zeep.org/c}item")
    elm("x")

    elm = schema.get_type("{http://tests.python-zeep.org/a}refs")
    elm(ref_elm="foo", ref_attr="bar")


def test_cyclic_imports():
    schema_a = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/a"
            targetNamespace="http://tests.python-zeep.org/a"
            xmlns:b="http://tests.python-zeep.org/b"
            elementFormDefault="qualified">

            <xs:import
                schemaLocation="http://tests.python-zeep.org/b.xsd"
                namespace="http://tests.python-zeep.org/b"/>
        </xs:schema>
    """.strip()
    )

    schema_b = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/b"
            targetNamespace="http://tests.python-zeep.org/b"
            xmlns:c="http://tests.python-zeep.org/c"
            elementFormDefault="qualified">

            <xs:import
                schemaLocation="http://tests.python-zeep.org/c.xsd"
                namespace="http://tests.python-zeep.org/c"/>
        </xs:schema>
    """.strip()
    )

    schema_c = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/c"
            targetNamespace="http://tests.python-zeep.org/c"
            elementFormDefault="qualified">

            <xs:import
                schemaLocation="http://tests.python-zeep.org/a.xsd"
                namespace="http://tests.python-zeep.org/a"/>
        </xs:schema>
    """.strip()
    )

    transport = DummyTransport()
    transport.bind("http://tests.python-zeep.org/a.xsd", schema_a)
    transport.bind("http://tests.python-zeep.org/b.xsd", schema_b)
    transport.bind("http://tests.python-zeep.org/c.xsd", schema_c)
    xsd.Schema(
        schema_a, transport=transport, location="http://tests.python-zeep.org/a.xsd"
    )


def test_get_type_through_import():
    schema_a = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/a"
            targetNamespace="http://tests.python-zeep.org/a"
            xmlns:b="http://tests.python-zeep.org/b"
            elementFormDefault="qualified">

            <xs:import
                schemaLocation="http://tests.python-zeep.org/b.xsd"
                namespace="http://tests.python-zeep.org/b"/>
            <xs:element name="foo" type="b:bar"/>

        </xs:schema>
    """.strip()
    )

    schema_b = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/b"
            targetNamespace="http://tests.python-zeep.org/b"
            xmlns:c="http://tests.python-zeep.org/c"
            elementFormDefault="qualified">

            <xs:complexType name="bar"/>

        </xs:schema>
    """.strip()
    )

    transport = DummyTransport()
    transport.bind("http://tests.python-zeep.org/a.xsd", schema_a)
    transport.bind("http://tests.python-zeep.org/b.xsd", schema_b)
    xsd.Schema(schema_a, transport=transport)


def test_duplicate_target_namespace():
    schema_a = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/a"
            targetNamespace="http://tests.python-zeep.org/a"
            elementFormDefault="qualified">

            <xs:import
                schemaLocation="http://tests.python-zeep.org/b.xsd"
                namespace="http://tests.python-zeep.org/duplicate"/>
            <xs:import
                schemaLocation="http://tests.python-zeep.org/c.xsd"
                namespace="http://tests.python-zeep.org/duplicate"/>
        </xs:schema>
    """.strip()
    )

    schema_b = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xsd:schema
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/duplicate"
            targetNamespace="http://tests.python-zeep.org/duplicate"
            elementFormDefault="qualified">
            <xsd:element name="elm-in-b" type="tns:item-c"/>
            <xsd:complexType name="item-c">
              <xsd:sequence>
                <xsd:element name="item-a" type="xsd:string"/>
                <xsd:element name="item-b" type="xsd:string"/>
              </xsd:sequence>
            </xsd:complexType>
        </xsd:schema>
    """.strip()
    )

    schema_c = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xsd:schema
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/duplicate"
            targetNamespace="http://tests.python-zeep.org/duplicate"
            elementFormDefault="qualified">
            <xsd:element name="elm-in-c" type="tns:item-c"/>
            <xsd:complexType name="item-c">
              <xsd:sequence>
                <xsd:element name="item-a" type="xsd:string"/>
                <xsd:element name="item-b" type="xsd:string"/>
              </xsd:sequence>
            </xsd:complexType>

        </xsd:schema>
    """.strip()
    )

    transport = DummyTransport()
    transport.bind("http://tests.python-zeep.org/a.xsd", schema_a)
    transport.bind("http://tests.python-zeep.org/b.xsd", schema_b)
    transport.bind("http://tests.python-zeep.org/c.xsd", schema_c)
    schema = xsd.Schema(schema_a, transport=transport)

    elm_b = schema.get_element("{http://tests.python-zeep.org/duplicate}elm-in-b")
    elm_c = schema.get_element("{http://tests.python-zeep.org/duplicate}elm-in-c")
    assert not isinstance(elm_b.type, UnresolvedType)
    assert not isinstance(elm_c.type, UnresolvedType)


def test_multiple_no_namespace():
    node_a = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xsd:schema
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/a"
            targetNamespace="http://tests.python-zeep.org/a"
            elementFormDefault="qualified">

          <xsd:import schemaLocation="http://tests.python-zeep.org/b.xsd"/>
          <xsd:import schemaLocation="http://tests.python-zeep.org/c.xsd"/>
        </xsd:schema>
    """.strip()
    )

    node_b = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xsd:schema
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            elementFormDefault="qualified">
        </xsd:schema>
    """.strip()
    )

    transport = DummyTransport()
    transport.bind("http://tests.python-zeep.org/b.xsd", node_b)
    transport.bind("http://tests.python-zeep.org/c.xsd", node_b)
    xsd.Schema(node_a, transport=transport)


def test_multiple_only_target_ns():
    node_a = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xsd:schema
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/a"
            targetNamespace="http://tests.python-zeep.org/a"
            elementFormDefault="qualified">

          <xsd:import schemaLocation="http://tests.python-zeep.org/b.xsd"/>
          <xsd:import schemaLocation="http://tests.python-zeep.org/c.xsd"/>
        </xsd:schema>
    """.strip()
    )

    node_b = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xsd:schema
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            elementFormDefault="qualified"
            targetNamespace="http://tests.python-zeep.org/duplicate-ns">
        </xsd:schema>
    """.strip()
    )

    transport = DummyTransport()
    transport.bind("http://tests.python-zeep.org/b.xsd", node_b)
    transport.bind("http://tests.python-zeep.org/c.xsd", node_b)
    xsd.Schema(node_a, transport=transport)


def test_schema_error_handling():
    node_a = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/a"
            targetNamespace="http://tests.python-zeep.org/a"
            xmlns:b="http://tests.python-zeep.org/b"
            elementFormDefault="qualified">

        </xs:schema>
    """.strip()
    )
    transport = DummyTransport()
    schema = xsd.Schema(node_a, transport=transport)

    with pytest.raises(ValueError):
        schema.get_element("nonexisting:something")
    with pytest.raises(ValueError):
        schema.get_type("nonexisting:something")
    with pytest.raises(exceptions.NamespaceError):
        schema.get_element("{nonexisting}something")
    with pytest.raises(exceptions.NamespaceError):
        schema.get_type("{nonexisting}something")
    with pytest.raises(exceptions.LookupError):
        schema.get_element("ns0:something")
    with pytest.raises(exceptions.LookupError):
        schema.get_type("ns0:something")


def test_schema_import_xmlsoap():
    node_a = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xsd:schema
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/a"
            targetNamespace="http://tests.python-zeep.org/a"
            xmlns:b="http://tests.python-zeep.org/b"
            elementFormDefault="qualified">
          <xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
        </xsd:schema>
    """.strip()
    )
    transport = DummyTransport()
    xsd.Schema(node_a, transport=transport)


def test_schema_import_unresolved():
    node_a = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xsd:schema
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/a"
            targetNamespace="http://tests.python-zeep.org/a"
            xmlns:b="http://tests.python-zeep.org/b"
            elementFormDefault="qualified">
          <xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
        </xsd:schema>
    """.strip()
    )
    transport = DummyTransport()
    xsd.Schema(node_a, transport=transport)


def test_no_target_namespace():
    node_a = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xsd:schema
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/a"
            targetNamespace="http://tests.python-zeep.org/a"
            elementFormDefault="qualified">

          <xsd:import schemaLocation="http://tests.python-zeep.org/b.xsd"/>

          <xsd:element name="container">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element ref="bla"/>
              </xsd:sequence>
            </xsd:complexType>
          </xsd:element>

        </xsd:schema>
    """.strip()
    )

    node_b = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xsd:schema
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            elementFormDefault="qualified">
            <xsd:element name="bla" type="xsd:string"/>
        </xsd:schema>
    """.strip()
    )

    transport = DummyTransport()
    transport.bind("http://tests.python-zeep.org/b.xsd", node_b)
    xsd.Schema(node_a, transport=transport)


def test_include_recursion():
    node_a = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/a"
            targetNamespace="http://tests.python-zeep.org/a"
            xmlns:b="http://tests.python-zeep.org/b"
            elementFormDefault="qualified">

            <xs:import
                schemaLocation="http://tests.python-zeep.org/b.xsd"
                namespace="http://tests.python-zeep.org/b"/>

        </xs:schema>
    """.strip()
    )

    node_b = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/b"
            targetNamespace="http://tests.python-zeep.org/b"
            elementFormDefault="qualified">

            <xs:include schemaLocation="http://tests.python-zeep.org/c.xsd"/>
            <xs:element name="bar" type="xs:string"/>
        </xs:schema>
    """.strip()
    )

    node_c = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/b"
            targetNamespace="http://tests.python-zeep.org/b"
            elementFormDefault="qualified">

            <xs:include schemaLocation="http://tests.python-zeep.org/b.xsd"/>

            <xs:element name="foo" type="xs:string"/>
        </xs:schema>
    """.strip()
    )

    transport = DummyTransport()
    transport.bind("http://tests.python-zeep.org/b.xsd", node_b)
    transport.bind("http://tests.python-zeep.org/c.xsd", node_c)

    schema = xsd.Schema(node_a, transport=transport)
    schema.get_element("{http://tests.python-zeep.org/b}foo")
    schema.get_element("{http://tests.python-zeep.org/b}bar")


def test_include_relative():
    node_a = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns="http://tests.python-zeep.org/tns"
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://tests.python-zeep.org/a"
            elementFormDefault="qualified">

            <xs:include schemaLocation="http://tests.python-zeep.org/subdir/b.xsd"/>

        </xs:schema>
    """.strip()
    )

    node_b = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
            <xs:include schemaLocation="c.xsd"/>
            <xs:element name="bar" type="xs:string"/>
        </xs:schema>
    """.strip()
    )

    node_c = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
            <xs:element name="foo" type="xs:string"/>
        </xs:schema>
    """.strip()
    )

    transport = DummyTransport()
    transport.bind("http://tests.python-zeep.org/subdir/b.xsd", node_b)
    transport.bind("http://tests.python-zeep.org/subdir/c.xsd", node_c)

    schema = xsd.Schema(node_a, transport=transport)
    schema.get_element("{http://tests.python-zeep.org/a}foo")
    schema.get_element("{http://tests.python-zeep.org/a}bar")


def test_include_no_default_namespace():
    node_a = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns="http://tests.python-zeep.org/tns"
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://tests.python-zeep.org/tns"
            elementFormDefault="qualified">

            <xs:include
                schemaLocation="http://tests.python-zeep.org/b.xsd"/>

            <xs:element name="container" type="foo"/>
        </xs:schema>
    """.strip()
    )

    # include without default namespace, other xsd prefix
    node_b = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xsd:schema
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/b">

            <xsd:simpleType name="my-string">
              <xsd:restriction base="xsd:boolean"/>
            </xsd:simpleType>

            <xsd:complexType name="foo">
              <xsd:sequence>
                <xsd:element name="item" type="my-string"/>
              </xsd:sequence>
            </xsd:complexType>
        </xsd:schema>
    """.strip()
    )

    transport = DummyTransport()
    transport.bind("http://tests.python-zeep.org/b.xsd", node_b)
    schema = xsd.Schema(node_a, transport=transport)
    item = schema.get_element("{http://tests.python-zeep.org/tns}container")
    assert item


def test_include_no_parent_default_namespace():
    schema_root = """
        <?xml version="1.0"?>
        <xs:schema xmlns="http://tests.python-zeep.org/rootns" xmlns:tns="http://tests.python-zeep.org/tns" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://tests.python-zeep.org/rootns" elementFormDefault="qualified">
            <xs:import namespace="http://tests.python-zeep.org/tns" schemaLocation="http://tests.python-zeep.org/tns.xsd"/>
            <xs:element name="root">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="container" type="tns:containerType" />
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
        </xs:schema>
    """.strip()

    # no default namespace, but targetNamespace
    schema_tns = """
        <?xml version="1.0"?>
        <xs:schema xmlns:tns="http://tests.python-zeep.org/tns" targetNamespace="http://tests.python-zeep.org/tns" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
            <xs:include schemaLocation="http://tests.python-zeep.org/include.xsd" />
        </xs:schema>
        """.strip()

    # no default namespace and no targetNamespace
    schema_include = """
        <?xml version="1.0"?>
        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
            <xs:complexType name="containerType">
                <xs:sequence>
                    <xs:element name="item" type="itemType" />
                </xs:sequence>
            </xs:complexType>
            <xs:complexType name="itemType">
              <xs:sequence>
                <xs:element name="intVal" type="xs:int" />
                <xs:element name="boolVal" type="xs:boolean" />
              </xs:sequence>
            </xs:complexType>
        </xs:schema>
        """.strip()

    class IncludeSchemaResolver(etree.Resolver):
        def resolve(self, url, id, context):
            if url == "http://tests.python-zeep.org/tns.xsd":
                return self.resolve_string(schema_tns, context)
            elif url == "http://tests.python-zeep.org/include.xsd":
                return self.resolve_string(schema_include, context)

    parser = etree.XMLParser()
    parser.resolvers.add(IncludeSchemaResolver())

    schema = etree.XMLSchema(etree.fromstring(schema_root, parser=parser))

    xml = """
        <?xml version="1.0"?>
        <root xmlns="http://tests.python-zeep.org/rootns">
            <container xmlns:tns="http://tests.python-zeep.org/tns">
                <tns:item>
                    <tns:intVal>42</tns:intVal>
                    <tns:boolVal>true</tns:boolVal>
                </tns:item>
            </container>
        </root>
    """.strip()

    xml = etree.fromstring(xml)
    schema.assertValid(xml)  # schema is ok for lxml

    schema_root = etree.fromstring(schema_root)
    schema_tns = etree.fromstring(schema_tns)
    schema_include = etree.fromstring(schema_include)

    transport = DummyTransport()
    transport.bind("http://tests.python-zeep.org/tns.xsd", schema_tns)
    transport.bind("http://tests.python-zeep.org/include.xsd", schema_include)
    xsd.Schema(schema_root, transport=transport)


def test_include_different_form_defaults():
    node_a = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns="http://tests.python-zeep.org/"
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://tests.python-zeep.org/">

            <xs:include
                schemaLocation="http://tests.python-zeep.org/b.xsd"/>
        </xs:schema>
    """.strip()
    )

    # include without default namespace, other xsd prefix
    node_b = load_xml(
        """
        <?xml version="1.0"?>
        <xsd:schema
            elementFormDefault="qualified"
            attributeFormDefault="qualified"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/b">

            <xsd:element name="container" type="foo"/>

            <xsd:complexType name="foo">
              <xsd:sequence>
                <xsd:element name="item" type="xsd:string"/>
              </xsd:sequence>
              <xsd:attribute name="attr" type="xsd:string"/>
            </xsd:complexType>
        </xsd:schema>
    """
    )

    transport = DummyTransport()
    transport.bind("http://tests.python-zeep.org/b.xsd", node_b)

    schema = xsd.Schema(node_a, transport=transport)
    item = schema.get_element("{http://tests.python-zeep.org/}container")
    obj = item(item="foo", attr="bar")
    node = render_node(item, obj)

    expected = load_xml(
        """
        <document>
          <ns0:container xmlns:ns0="http://tests.python-zeep.org/" ns0:attr="bar">
            <ns0:item>foo</ns0:item>
          </ns0:container>
        </document>
    """
    )
    assert_nodes_equal(expected, node)


def test_merge():
    node_a = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/a"
            targetNamespace="http://tests.python-zeep.org/a"
            xmlns:b="http://tests.python-zeep.org/b"
            elementFormDefault="qualified">
          <xs:element name="foo" type="xs:string"/>
        </xs:schema>
    """.strip()
    )

    node_b = etree.fromstring(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/b"
            targetNamespace="http://tests.python-zeep.org/b"
            elementFormDefault="qualified">
          <xs:element name="foo" type="xs:int"/>
        </xs:schema>
    """.strip()
    )

    schema_a = xsd.Schema(node_a)
    schema_b = xsd.Schema(node_b)
    schema_a.merge(schema_b)

    schema_a.get_element("{http://tests.python-zeep.org/a}foo")
    schema_a.get_element("{http://tests.python-zeep.org/b}foo")


def test_xml_namespace():
    xmlns = load_xml(
        """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:xml="http://www.w3.org/XML/1998/namespace"
            targetNamespace="http://www.w3.org/XML/1998/namespace"
            elementFormDefault="qualified">
          <xs:attribute name="lang" type="xs:string"/>
        </xs:schema>
    """
    )

    transport = DummyTransport()
    transport.bind("http://www.w3.org/2001/xml.xsd", xmlns)

    xsd.Schema(
        load_xml(
            """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://tests.python-zeep.org/"
            targetNamespace="http://tests.python-zeep.org/"
            elementFormDefault="qualified">
          <xs:import namespace="http://www.w3.org/XML/1998/namespace"
                     schemaLocation="http://www.w3.org/2001/xml.xsd"/>
          <xs:element name="container">
            <xs:complexType>
              <xs:sequence/>
              <xs:attribute ref="xml:lang"/>
            </xs:complexType>
          </xs:element>
        </xs:schema>
    """
        ),
        transport=transport,
    )


def test_auto_import_known_schema():
    content = open("tests/wsdl_files/soap-enc.xsd", "rb").read()

    transport = DummyTransport()
    transport.bind("http://schemas.xmlsoap.org/soap/encoding/", content)

    schema = xsd.Schema(
        load_xml(
            """
        <?xml version="1.0"?>
        <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/"
            xmlns:tns="http://tests.python-zeep.org/"
            targetNamespace="http://tests.python-zeep.org/"
            elementFormDefault="qualified">
          <xs:import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
          <xs:group ref="soap-enc:Struct"/>
        </xs:schema>
    """
        ),
        transport=transport,
    )
    schema.set_ns_prefix("soap-enc", "http://schemas.xmlsoap.org/soap/encoding/")
    schema.get_group("soap-enc:Struct")
