# Check deterministic transport, semantic state equality and reloaded validation.
import importlib
from pathlib import Path

import pytest

from hearthwright import Building, Design, Volume, generate, validate, blockstates_equivalent
from hearthwright.blocks import NBTFile
from scripts.sample_batch import CLIENTS


def test_semantic_property_order_is_not_a_change():
    a = 'minecraft:oak_stairs[facing=north,half=bottom,shape=straight]'
    b = 'minecraft:oak_stairs[shape=straight,half=bottom,facing=north]'
    assert blockstates_equivalent(a, b)
    assert not blockstates_equivalent(a, b.replace('north', 'south'))
    assert not blockstates_equivalent(a, a.replace(',shape=straight', ''))
    assert not blockstates_equivalent(a, a.replace('oak_stairs', 'spruce_stairs'))
    with pytest.raises((TypeError, ValueError)):
        blockstates_equivalent(a, None)


def test_export_is_byte_deterministic_with_overwrites(artifact_dir):
    design = Design('Determinism', (Volume('home'),))
    a, b = generate(design, 73), generate(design, 73)
    p = next(iter(a.grid.blocks))
    original = a.grid.get(*p)
    a.grid.put(*p, 'gold_block')
    a.grid.put(*p, original)
    first, second = artifact_dir / 'a.litematic', artifact_dir / 'b.litematic'
    a.export(first)
    b.export(second)
    assert first.read_bytes() == second.read_bytes()
    assert a.grid.blocks == b.grid.blocks
    a.verify(first)


@pytest.mark.parametrize('client', CLIENTS)
def test_reloaded_samples_validate_with_explicit_metadata(client, artifact_dir):
    design = importlib.import_module('examples.' + client).DESIGN
    building = generate(design, 1)
    path = artifact_dir / f'{client}.litematic'
    building.export(path)
    building.verify(path)
    assert validate(path, path.with_suffix('.json')).valid
    loaded = Building.load(path, building.metadata)
    assert loaded.validate().valid
    assert loaded.grid.inventories


def test_reload_detects_unexpected_or_changed_geometry(artifact_dir):
    design = Design('Roundtrip damage', (Volume('home'),))
    b = generate(design, 0)
    path = artifact_dir / 'build.litematic'
    b.export(path)
    loaded = Building.load(path)
    p = b.metadata['rooms'][0]['anchor']
    loaded.grid.put(*p, 'stone')
    loaded.grid.export(path)
    with pytest.raises(AssertionError, match='coordinate set|changed on reload'):
        b.verify(path)


def test_inventory_corruption_is_observed_in_nbt(artifact_dir):
    b = generate(Design('Stocked home', (Volume('home'),)), 0)
    path = artifact_dir / 'stock.litematic'
    b.export(path)
    nbt = NBTFile.load_regardless_of_gzipped(path)
    region = next(iter(nbt['Regions'].values()))
    for tile in region['TileEntities']:
        if 'Items' in tile and len(tile['Items']):
            del tile['Items'][:]
            break
    NBTFile(nbt).save(path)
    assert 'inventory.changed' in {d.rule for d in validate(path).diagnostics}


def test_harness_client_runs_from_another_directory(artifact_dir):
    import subprocess
    import sys
    root = Path(__file__).resolve().parents[1]
    subprocess.run([sys.executable, str(root / 'generate.py'), '--seed', '73'], cwd=artifact_dir, check=True, capture_output=True)
    default_path = artifact_dir / 'output.litematic'
    assert default_path.exists()
    assert validate(default_path).valid
    second = artifact_dir / 'explicit.litematic'
    subprocess.run([sys.executable, str(root / 'generate.py'), '--seed', '73', '--output', str(second)], cwd=artifact_dir, check=True, capture_output=True)
    assert default_path.read_bytes() == second.read_bytes()
