# Deliberately damage independent aspects of final geometry to audit rule sensitivity.
import pytest

from hearthwright import Design, Volume, generate
from hearthwright.blocks import name


def rules(building):
    return {d.rule for d in building.validate().diagnostics}


def test_bad_door_half_is_rejected():
    b = generate(Design('Door test', (Volume('home'),)), 0)
    p = next(p for p, s in b.grid.blocks.items() if '_door[' in s and 'half=upper' in s)
    b.grid.put(*p, 'spruce_door', half='upper', facing='east')
    assert 'multiblock.door' in rules(b)


def test_missing_bed_half_and_support_are_rejected():
    b = generate(Design('Bed test', (Volume('home'),)), 0)
    p = next(p for p, s in b.grid.blocks.items() if '_bed[' in s and 'part=head' in s)
    b.grid.put(*p, 'air')
    assert 'multiblock.bed' in rules(b)
    foot = next(p for p, s in b.grid.blocks.items() if '_bed[' in s)
    b.grid.put(foot[0], foot[1] - 1, foot[2], 'air')
    assert 'support.bed' in rules(b)


def test_blocked_stair_headroom_and_unreachable_level_are_rejected():
    from examples.stilted_manor import DESIGN
    b = generate(DESIGN, 0)
    stairs = [s for s in b.metadata['stairs'] if 'manor/0->' in s['owner']]
    for s in stairs:
        x, y, z = s['point']
        b.grid.put(x, y + 1, z, 'stone')
        b.grid.put(x, y + 2, z, 'stone')
    result = rules(b)
    assert 'clearance.route' in result
    assert 'reachability.room' in result


def test_stair_facing_and_tread_support_are_rejected():
    from examples.red_roof_farmstead import DESIGN
    b = generate(DESIGN, 0)
    s = b.metadata['stairs'][2]
    x, y, z = s['point']
    b.grid.stair(x, y, z, facing='north')
    b.grid.put(x, y - 1, z, 'air')
    assert {'circulation.stair', 'support.stair'} <= rules(b)


def test_missing_stilt_segment_is_rejected():
    from examples.stilt_water_cabin import DESIGN
    b = generate(DESIGN, 0)
    support = b.metadata['supports'][0]
    x, y, z = support['bottom']
    b.grid.put(x, y + 1, z, 'water', level='0')
    assert 'support.column' in rules(b)


def test_ladder_attachment_is_checked():
    from examples.stone_cottage import DESIGN
    b = generate(DESIGN, 0)
    p = next(p for p, state in b.grid.blocks.items() if name(state) == 'ladder')
    b.grid.put(p[0] - 1, p[1], p[2], 'air')
    assert 'support.ladder' in rules(b)


def test_wall_hole_and_blocked_glazing_are_rejected():
    b = generate(Design('Envelope test', (Volume('home'),)), 0)
    window = next(w for w in b.metadata['openings'] if w['kind'] == 'window')
    p = window['cells'][0]
    b.grid.put(*p, 'air')
    assert {'opening.glazing', 'envelope.leak'} <= rules(b)


def test_roof_hole_is_rejected():
    from examples.stone_cottage import DESIGN
    b = generate(DESIGN, 0)
    room = b.metadata['rooms'][-1]
    x, y, z = room['anchor']
    roof_y = b.grid.bounds[4]
    for yy in range(y + 2, roof_y + 1):
        b.grid.put(x, yy, z, 'air')
    assert 'envelope.leak' in rules(b)


def test_remove_room_light_and_change_furniture():
    b = generate(Design('Lighting test', (Volume('home'),)), 0)
    lamp = next(record for record in b.metadata['lights'] if '/room' in record['owner'])
    b.grid.put(*lamp['point'], 'air')
    assert {'lighting.room', 'furnishing.changed'} <= rules(b)


def test_unknown_block_state_and_out_of_recorded_bounds(artifact_dir):
    b = generate(Design('Bounds test', (Volume('home'),)), 0)
    b.export(artifact_dir / 'bounds.litematic')
    b.grid.blocks[1000, 1000, 1000] = 'minecraft:oak_log[axis=invalid]'
    result = rules(b)
    assert {'block.state', 'bounds.export', 'bounds.resources'} <= result


def test_isolated_usable_floor_is_detected_even_when_anchor_remains_reachable():
    from hearthwright import Level
    from hearthwright.navigation import walkable
    from hearthwright.materials import DIRECTIONS
    b = generate(Design('Floor pocket', (Volume('home', width=13, depth=13, levels=(Level(('living',)),)),)), 7)
    room = b.metadata['rooms'][0]
    choices = [(x, room['floor'] + 1, z) for x, z in room['cells'] if walkable(b.grid, (x, room['floor'] + 1, z))]
    p = max(choices, key=lambda p: abs(p[0] - room['anchor'][0]) + abs(p[2] - room['anchor'][2]))
    for dx, dz in DIRECTIONS.values():
        b.grid.box((p[0] + dx, p[1], p[2] + dz, p[0] + dx, p[1] + 1, p[2] + dz), 'stone')
    assert 'reachability.floor' in rules(b)


def test_exterior_landing_must_connect_to_terrain():
    b = generate(Design('Terrain transition', (Volume('home'),)), 0)
    x, y, z = b.metadata['start']
    b.grid.put(x, y - 1, z + 1, 'air')
    assert 'reachability.terrain' in rules(b)
