"""Differential test: prove the dataclass ``Serializable`` reproduces the ORIGINAL
pydantic behaviour byte-for-byte across a large, diverse corpus.

``serializable_diff_expected.json`` was generated by running the corpus through the
**pydantic** implementation (at the pre-refactor commit). This test replays every
case through the current (dataclass) code and asserts:

  1. ``model_validate(wire).dump()``      == the pydantic dump  (values)
  2. ``model_validate(wire).dump_json()`` == the pydantic dump_json  (bytes + order)
  3. round-trip idempotence: ``model_validate(dump).dump()`` == dump

This covers what the hand-picked golden cannot: deserialization of many shapes
(incl. legacy context dicts without a ``type`` field, snake/camel keys, optionals
present/absent, unicode, every context + message + Execution variant), which are
the code paths only exercised in production by the DB/queue/editor boundaries.

To regenerate after an INTENTIONAL wire change, re-run the generator against the
pydantic baseline — never hand-edit the expected file to make this pass.
"""

import json
import os
import unittest

from abstra_internals.entities._golden.serializable_diff_corpus import wire_cases
from abstra_internals.entities.execution import Execution
from abstra_internals.entities.execution_context import (
    CodeSnippetContext,
    CodeSnippetExecutionMock,
    FormContext,
    FormExecutionMock,
    HookContext,
    HookExecutionMock,
    JobContext,
    JobExecutionMock,
    PageContext,
    PageExecutionMock,
    Request,
    Response,
    ScriptContext,
    ScriptExecutionMock,
)
from abstra_internals.repositories.models import (
    PingMessage,
    PreExecution,
    RunSnippetMessage,
    RunSnippetSandboxedMessage,
    StopAllExecutionsMessage,
    StopExecutionMessage,
)
from abstra_internals.repositories.tasks import (
    ExecutionTasksResponse,
    TaskDTO,
    TaskEventDetails,
)

REGISTRY = {
    c.__name__: c
    for c in [
        Request,
        Response,
        HookExecutionMock,
        FormExecutionMock,
        ScriptExecutionMock,
        JobExecutionMock,
        PageExecutionMock,
        CodeSnippetExecutionMock,
        HookContext,
        FormContext,
        ScriptContext,
        JobContext,
        PageContext,
        CodeSnippetContext,
        TaskDTO,
        TaskEventDetails,
        ExecutionTasksResponse,
        PreExecution,
        StopExecutionMessage,
        StopAllExecutionsMessage,
        RunSnippetMessage,
        RunSnippetSandboxedMessage,
        PingMessage,
        Execution,
    ]
}

_EXPECTED = os.path.join(
    os.path.dirname(__file__), "_golden", "serializable_diff_expected.json"
)


class SerializableDiffTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        with open(_EXPECTED, encoding="utf-8") as f:
            cls.expected = json.load(f)

    def test_corpus_in_sync_with_frozen_expected(self):
        # If the corpus changed, the frozen pydantic expected must be regenerated.
        self.assertEqual(
            len(self.expected),
            len(wire_cases()),
            "corpus changed without regenerating serializable_diff_expected.json",
        )

    def test_dump_matches_pydantic(self):
        for i, e in enumerate(self.expected):
            with self.subTest(i=i, cls=e["class"]):
                cls = REGISTRY[e["class"]]
                inst = cls.model_validate(e["wire"])
                self.assertEqual(inst.dump(), e["dump"])

    def test_dump_json_matches_pydantic_bytes(self):
        for i, e in enumerate(self.expected):
            with self.subTest(i=i, cls=e["class"]):
                cls = REGISTRY[e["class"]]
                inst = cls.model_validate(e["wire"])
                self.assertEqual(inst.dump_json(), e["dump_json"])

    def test_roundtrip_idempotent(self):
        for i, e in enumerate(self.expected):
            with self.subTest(i=i, cls=e["class"]):
                cls = REGISTRY[e["class"]]
                # dump -> validate -> dump must be a fixed point.
                once = cls.model_validate(e["wire"]).dump()
                twice = cls.model_validate(once).dump()
                self.assertEqual(once, twice)
                self.assertEqual(twice, e["dump"])


if __name__ == "__main__":
    unittest.main()
