# Functional bays must preserve their own access, room circulation, support and inventories.
import pytest
from hearth import Scene, Box, ContractError
from hearth.environment import Terrain, gradient
from hearth.kernel import validate
from hearth.blocks import state_of, name_of
from hearth.components.primitives import Volume
from hearth.persistence import export_scene, load_scene, semantic_digest, encode
from hearth_extensions import Pavilion, Polygon
from hearth_extensions.interiors import FUNCTION_GROUPS
from test_extension_failures import damage, snapshot


def furnished_scene(purpose='library', width=13):
    scene = Scene(23, domain=Box((-12, -4, -12), (40, 60, 40)))
    scene.place('land', Terrain(scene.domain, gradient(base=3)))
    scene.place('hall', Pavilion(Polygon.chamfered(width, 17, 4), '/land', purpose, ('north', 'south', 'east', 'west')))
    return scene.finalize()


@pytest.mark.parametrize('purpose', tuple(FUNCTION_GROUPS))
def test_functional_bays_are_reachable_and_preserve_inventory(purpose, work):
    scene = furnished_scene(purpose)
    bays = [n for n in scene.nodes.values() if n.type == 'extension.work-bay']
    assert len(bays) >= 2
    assert not validate(scene, True)
    for bay in bays:
        port = scene.view.port(bay.path, 'use')
        assert scene.view.clear(port.region)
        assert any(scene.blocks[p].nbt for p in scene.cells(bay.path))
    path = work / (purpose + '.litematic')
    export_scene(scene, path)
    restored = load_scene(path)
    assert encode(restored.nodes) == encode(scene.nodes)
    assert semantic_digest(restored) == semantic_digest(scene)


def test_larger_hall_allocates_more_bays_and_protects_central_aisle():
    small = furnished_scene(width=13)
    large = furnished_scene(width=25)
    small_decisions = small.nodes['/hall/furnishings'].contract.decisions
    large_decisions = large.nodes['/hall/furnishings'].contract.decisions
    assert large_decisions['placed_bays'] > small_decisions['placed_bays']
    assert large_decisions['omitted_optional'] == 0
    point = large.nodes['/hall/furnishings'].contract.rules[0].cells[20]
    before = snapshot(large)
    with pytest.raises(ContractError, match='clearance'):
        large.place('crate', Volume(Box(point, point), state_of('stone')))
    assert snapshot(large) == before


def test_damaged_station_furnishing_and_support_are_detected():
    scene = furnished_scene('library')
    bay = next(n for n in scene.nodes.values() if n.type == 'extension.work-bay')
    lectern = next(p for p in scene.cells(bay.path) if name_of(scene.blocks[p].state) == 'lectern')
    damage(scene, lectern)
    assert any(d.rule == 'furnishing' and d.component == bay.path for d in validate(scene, True))
    bed = bay.contract.rules[0].cells[0]
    damage(scene, bed)
    assert any(d.rule == 'support' and d.position == bed for d in validate(scene, True))


def test_interaction_clearance_remains_protected_after_completion():
    scene = furnished_scene('bedroom')
    bay = next(n for n in scene.nodes.values() if n.type == 'extension.work-bay')
    at = scene.view.port(bay.path, 'use').frame.origin
    before = snapshot(scene)
    with pytest.raises(ContractError, match='clearance'):
        scene.place('block-user', Volume(Box(at, at), state_of('stone')))
    assert snapshot(scene) == before
