# A parent cut refilled by descendants must undo back to the original host cell.
from dataclasses import dataclass
from hearth import Scene, Box, Frame, Binding
from hearth.kernel import Capability, Contract, Port, Grant, Plan, Child
from hearth.blocks import AIR, state_of
from hearth.components.primitives import Volume
from hearth.persistence import semantic_digest, export_scene, load_scene

CELL = Box((0, 0, 0), (0, 0, 0))


@dataclass(frozen=True)
class Host:

    def capability(self):
        return Capability('test.host', ('host',))

    def negotiate(self, ctx, parameters):
        return Contract(CELL, (Port('socket', 'socket', Frame(), CELL, 1, 'edit'),), (Grant('edit', CELL, ('install',), ('cut',), 1),))

    def realize(self, ctx, contract):
        plan = Plan()
        plan.fill(CELL, state_of('stone'))
        return plan


@dataclass(frozen=True)
class CutAndRefill:

    def capability(self):
        return Capability('test.cut-and-refill', ('cut', 'composite'))

    def negotiate(self, ctx, parameters):
        return Contract(CELL)

    def realize(self, ctx, contract):
        plan = Plan(children=[Child('refill', Volume(CELL, state_of('glass')))])
        plan.block((0, 0, 0), AIR, 'cut')
        return plan


def test_nested_cut_refill_undo_is_synchronized_before_and_after_reload(work):
    scene = Scene(0, domain=CELL.expand(3))
    scene.place('host', Host())
    before = semantic_digest(scene)
    scene.place('replacement', CutAndRefill(), bindings=(Binding('/host', 'socket'),))
    assert scene.view.owner((0, 0, 0)) == '/replacement/refill'
    path = work / 'nested.litematic'
    export_scene(scene.finalize(), path)
    restored = load_scene(path)
    for candidate in (scene, restored):
        candidate.remove('/replacement')
        assert semantic_digest(candidate) == before
        assert candidate.cells('/host') == {(0, 0, 0)}
        assert candidate.inspect((0, 0, 0))['instance'] == '/host'
        assert not any(p.startswith('/replacement') for p in candidate.nodes)
        assert not candidate.cells('/replacement', True)
