"""XSD validation tests for IUCLID XML builders — no DB required."""

import xml.etree.ElementTree as ET

from django.test import SimpleTestCase, tag

from epiuclid.builders.base import NS_PLATFORM_CONTAINER
from epiuclid.builders.endpoint_study import EndpointStudyRecordBuilder
from epiuclid.builders.reference_substance import ReferenceSubstanceBuilder
from epiuclid.builders.substance import SubstanceBuilder
from epiuclid.schemas.loader import get_content_schema, get_document_schema

from .factories import (
    make_endpoint_study_record_data,
    make_half_life_entry,
    make_reference_substance_data,
    make_soil_properties_data,
    make_substance_data,
    make_transformation_entry,
)


def _content_element(xml_str: str) -> ET.Element:
    """Extract the first child of <Content> from a full i6d XML string."""
    root = ET.fromstring(xml_str)
    content = root.find(f"{{{NS_PLATFORM_CONTAINER}}}Content")
    assert content is not None and len(content) > 0
    return list(content)[0]


def _assert_content_valid(xml_str: str, doc_type: str, subtype: str | None = None) -> None:
    schema = get_content_schema(doc_type, subtype)
    schema.validate(_content_element(xml_str))


@tag("iuclid")
class SubstanceXSDValidationTest(SimpleTestCase):
    def test_substance_validates_against_xsd(self):
        data = make_substance_data()
        xml_str = SubstanceBuilder().build(data)
        _assert_content_valid(xml_str, "SUBSTANCE")

    def test_minimal_substance_validates_against_xsd(self):
        data = make_substance_data(name="Unknown compound", reference_substance_uuid=None)
        xml_str = SubstanceBuilder().build(data)
        _assert_content_valid(xml_str, "SUBSTANCE")


@tag("iuclid")
class ReferenceSubstanceXSDValidationTest(SimpleTestCase):
    def test_reference_substance_validates_against_xsd(self):
        data = make_reference_substance_data()
        xml_str = ReferenceSubstanceBuilder().build(data)
        _assert_content_valid(xml_str, "REFERENCE_SUBSTANCE")

    def test_reference_substance_minimal_validates_against_xsd(self):
        data = make_reference_substance_data(
            name="Minimal compound",
            smiles="CC",
            cas_number=None,
            molecular_formula=None,
            molecular_weight=None,
            inchi=None,
            inchi_key=None,
        )
        xml_str = ReferenceSubstanceBuilder().build(data)
        _assert_content_valid(xml_str, "REFERENCE_SUBSTANCE")


@tag("iuclid")
class EndpointStudyRecordXSDValidationTest(SimpleTestCase):
    def test_endpoint_study_record_validates_against_xsd(self):
        data = make_endpoint_study_record_data(
            name="Biodegradation study with data",
            half_lives=[
                make_half_life_entry(),
            ],
            temperature=(20.0, 20.0),
            transformation_products=[
                make_transformation_entry(),
            ],
            model_name_and_version=["Test model 1.0"],
            software_name_and_version=["enviPath"],
            model_remarks=["Model UUID: 00000000-0000-0000-0000-000000000000"],
        )
        xml_str = EndpointStudyRecordBuilder().build(data)
        _assert_content_valid(xml_str, "ENDPOINT_STUDY_RECORD", "BiodegradationInSoil")

    def test_temperature_only_esr_validates_against_xsd(self):
        data = make_endpoint_study_record_data(
            name="Biodegradation study with temperature only", temperature=(21.0, 21.0)
        )
        xml_str = EndpointStudyRecordBuilder().build(data)
        _assert_content_valid(xml_str, "ENDPOINT_STUDY_RECORD", "BiodegradationInSoil")

    def test_skeleton_esr_validates_against_xsd(self):
        data = make_endpoint_study_record_data(name="Biodegradation study")
        xml_str = EndpointStudyRecordBuilder().build(data)
        _assert_content_valid(xml_str, "ENDPOINT_STUDY_RECORD", "BiodegradationInSoil")

    def test_esr_with_soil_properties_validates_against_xsd(self):
        """ESR with full soil properties validates against BiodegradationInSoil XSD."""
        data = make_endpoint_study_record_data(
            name="Biodegradation study with soil properties",
            soil_properties=make_soil_properties_data(),
        )
        xml_str = EndpointStudyRecordBuilder().build(data)
        _assert_content_valid(xml_str, "ENDPOINT_STUDY_RECORD", "BiodegradationInSoil")

    def test_esr_with_multiple_soils_and_linked_dt_validates_against_xsd(self):
        data = make_endpoint_study_record_data(
            name="Biodegradation study with multiple soils",
            soil_properties_entries=[
                make_soil_properties_data(soil_no_code="2", soil_type="LOAMY_SAND"),
                make_soil_properties_data(soil_no_code="4", soil_type="CLAY_LOAM"),
            ],
            half_lives=[
                make_half_life_entry(dt50_start=1.0, dt50_end=1.0, soil_no_code="2"),
                make_half_life_entry(dt50_start=2.0, dt50_end=2.0, soil_no_code="4"),
            ],
        )
        xml_str = EndpointStudyRecordBuilder().build(data)
        _assert_content_valid(xml_str, "ENDPOINT_STUDY_RECORD", "BiodegradationInSoil")


@tag("iuclid")
class DocumentWrapperXSDValidationTest(SimpleTestCase):
    def test_full_i6d_document_validates_against_container_xsd(self):
        """Validate the Document wrapper (PlatformMetadata + Content + Attachments + ModificationHistory).

        The container schema uses processContents="strict" for xs:any in Content,
        so we need the content schema loaded into the validator too.
        """
        data = make_substance_data()
        xml_str = SubstanceBuilder().build(data)
        root = ET.fromstring(xml_str)

        doc_schema = get_document_schema()
        content_schema = get_content_schema("SUBSTANCE")

        # This is a xmlschema quirk and happens because there are children of the Content element not defined in the Content schema.
        errors = [
            e for e in doc_schema.iter_errors(root) if "unavailable namespace" not in str(e.reason)
        ]
        self.assertEqual(errors, [], msg=f"Document wrapper errors: {errors}")

        content_el = _content_element(xml_str)
        content_schema.validate(content_el)
