"""Verify a synthetic typed XML transformation; this is not a production XML parser."""
from dataclasses import dataclass, replace
from math import isfinite
import unittest
from xml.etree import ElementTree as ET

SAMPLE = '''<shape units="local">
  <vertex x="1" y="1"/><vertex x="4" y="1"/>
  <vertex x="4" y="3"/><vertex x="1" y="3"/>
  <creator display="Example" internalId="private-example"/>
</shape>'''


@dataclass(frozen=True)
class Shape:
    """Hold ordered local vertices and creator fields for the illustrative schema."""
    vertices: tuple[tuple[float, float], ...]
    display: str
    internal_id: str | None


def decode(xml: str) -> Shape:
    """Decode the fixed example schema, rejecting unsupported fields or invalid numbers."""
    root = ET.fromstring(xml)
    if root.tag != 'shape' or root.attrib != {'units': 'local'}:
        raise ValueError('Expected a local-coordinate shape.')
    vertices = []
    creator = None
    for child in root:
        if list(child) or (child.text or '').strip():
            raise ValueError('Nested or text content is unsupported.')
        if child.tag == 'vertex' and set(child.attrib) == {'x', 'y'}:
            point = (float(child.attrib['x']), float(child.attrib['y']))
            if not all(isfinite(value) for value in point):
                raise ValueError('Coordinates must be finite.')
            vertices.append(point)
        elif child.tag == 'creator' and creator is None:
            if 'display' not in child.attrib or set(child.attrib) - {'display', 'internalId'}:
                raise ValueError('Unsupported creator fields.')
            creator = child.attrib
        else:
            raise ValueError('Unsupported or duplicate element.')
    if len(vertices) != 4 or creator is None:
        raise ValueError('Expected four vertices and one creator.')
    return Shape(tuple(vertices), creator['display'], creator.get('internalId'))


def prepare_output(shape: Shape) -> Shape:
    """Return an independent output value without the optional internal identifier."""
    return replace(shape, internal_id=None)


def encode(shape: Shape) -> str:
    """Serialise the typed example while retaining vertex order and permitted attributes."""
    root = ET.Element('shape', units='local')
    for x, y in shape.vertices:
        ET.SubElement(root, 'vertex', x=str(x), y=str(y))
    attributes = {'display': shape.display}
    if shape.internal_id is not None:
        attributes['internalId'] = shape.internal_id
    ET.SubElement(root, 'creator', attributes)
    return ET.tostring(root, encoding='unicode')


class TransformationTests(unittest.TestCase):
    """Exercise preservation, intentional redaction and rejection in the synthetic schema."""

    def test_round_trip_preserves_attributes_and_vertex_order(self):
        """Retain every modelled value through a decode/encode/decode cycle."""
        # Arrange
        original = decode(SAMPLE)
        # Act
        actual = decode(encode(original))
        # Assert
        self.assertEqual(actual, original)
        self.assertEqual(actual.vertices, ((1, 1), (4, 1), (4, 3), (1, 3)))

    def test_output_policy_removes_only_the_optional_identifier(self):
        """Remove the selected attribute without changing permitted geometry or provenance."""
        # Arrange
        source = decode(SAMPLE)
        # Act
        prepared = prepare_output(source)
        transmitted = encode(prepared)
        # Assert
        self.assertEqual(decode(transmitted), prepared)
        self.assertNotIn('internalId', transmitted)
        self.assertEqual(prepared.vertices, source.vertices)
        self.assertEqual(prepared.display, source.display)
        self.assertEqual(source.internal_id, 'private-example')

    def test_unknown_fields_are_rejected(self):
        """Reject extensions absent from this example's typed schema."""
        # Arrange
        unexpected = SAMPLE.replace('</shape>', '<extension value="unmodelled"/></shape>')
        # Act / Assert
        with self.assertRaises(ValueError):
            decode(unexpected)

    def test_non_finite_coordinate_is_rejected(self):
        """Reject a non-finite coordinate before constructing the typed value."""
        # Arrange
        invalid = SAMPLE.replace('x="1"', 'x="NaN"', 1)
        # Act / Assert
        with self.assertRaises(ValueError):
            decode(invalid)


if __name__ == '__main__':
    unittest.main(verbosity=2)
