"""De-serialize the model, validate it and serialize it back."""
import argparse
import difflib
import json
import pathlib
import sys
from xml.dom import minidom

if sys.version_info >= (3, 9):
    from typing import Literal, Optional, Any
else:
    from typing_extensions import Literal

if sys.version_info >= (3, 11):
    from typing import assert_never
else:
    from typing_extensions import assert_never

# The module aas_core is expected to be generated by the integration test.
from sdk import types, jsonization, xmlization, verification


def normalize_xml(xml_text: str) -> str:
    """Normalize XML for comparison."""
    normalized = minidom.parseString(xml_text).toprettyxml(indent=" ")
    # Remove blank lines
    return "\n".join([line for line in normalized.splitlines() if line.strip()])


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--model_path", help="Path to the model in JSON or XML", required=True
    )
    args = parser.parse_args()

    model_path = pathlib.Path(args.model_path)

    if not model_path.exists():
        print(f"--model_path does not exist: {model_path}", file=sys.stderr)
        sys.exit(1)

    text = model_path.read_text(encoding="utf-8")
    stripped_text = text.strip()

    if len(stripped_text) == 0:
        print(f"Model {model_path} is empty.", file=sys.stderr)
        sys.exit(1)

    # NOTE (mristin):
    # We assume by convention that the main object in the integration test is defined as
    # the class ``Root``.
    root: types.Root

    first_character = stripped_text[0]

    jsonable = None  # type: Optional[Any]

    source_format: Literal["json", "xml"]

    if first_character == "{":
        try:
            jsonable = json.loads(text)
        except json.JSONDecodeError as exception:
            print(
                f"Failed to parse JSON from {model_path}: {exception}", file=sys.stderr
            )
            sys.exit(1)

        try:
            root = jsonization.root_from_jsonable(jsonable)
        except jsonization.DeserializationException as exception:
            print(f"Failed to parse {model_path}: {exception}", file=sys.stderr)
            sys.exit(1)

        source_format = "json"

    elif first_character == "<":
        try:
            root = xmlization.root_from_str(text)
        except xmlization.DeserializationException as exception:
            print(f"Failed to parse {model_path}: {exception}", file=sys.stderr)
            sys.exit(1)

        source_format = "xml"

    else:
        print(
            f"Unrecognized first non-whitespace character in {model_path}, "
            f"so we do not know how to parse the file: {first_character!r}",
            file=sys.stderr,
        )
        sys.exit(1)

    for error in verification.verify(root):
        print(
            f"The model from {model_path} failed to pass verification "
            f"at {error.path}: {error.cause}",
            file=sys.stderr,
        )
        sys.exit(1)

    if source_format == "json":
        another_jsonable = jsonization.to_jsonable(root)

        assert jsonable is not None

        original_pretty = json.dumps(jsonable, indent=2, ensure_ascii=False)
        another_pretty = json.dumps(another_jsonable, indent=2, ensure_ascii=False)

        if original_pretty != another_pretty:
            diff = "\n".join(
                difflib.unified_diff(
                    original_pretty.splitlines(),
                    another_pretty.splitlines(),
                    fromfile="original",
                    tofile="roundtrip",
                    lineterm="",
                )
            )
            print(
                f"JSON round-trip mismatch for {model_path}:\n{diff}",
                file=sys.stderr,
            )
            sys.exit(1)

    elif source_format == "xml":
        assert isinstance(root, types.Root)

        another_text = xmlization.to_str(root)

        if normalize_xml(text) != normalize_xml(another_text):
            diff = "\n".join(
                difflib.unified_diff(
                    normalize_xml(text).splitlines(),
                    normalize_xml(another_text).splitlines(),
                    fromfile="original",
                    tofile="roundtrip",
                    lineterm="",
                )
            )
            print(
                f"XML round-trip mismatch for {model_path}:\n{diff}",
                file=sys.stderr,
            )
            sys.exit(1)

    else:
        assert_never(source_format)


if __name__ == "__main__":
    main()
