# Validate a diverse finite seed corpus through independent high-level clients.
import importlib
from dataclasses import replace

import pytest

from hearthwright import Attachment, Design, Entrance, Volume, Level, Roof, Site, Materials, generate
from scripts.sample_batch import CLIENTS

SEEDS = (0, 1, 2, 3, 4, 5, 17, 101, 20260925, -1, 2**63 - 1, -(2**63))


@pytest.mark.parametrize('client', CLIENTS)
@pytest.mark.parametrize('seed', SEEDS)
def test_reference_and_novel_compositions(client, seed):
    design = importlib.import_module('examples.' + client).DESIGN
    building = generate(design, seed)
    report = building.validate()
    assert report.valid, report.to_dict()
    assert report.checked['rooms'] >= 1
    assert report.checked['furniture_groups'] >= len(building.metadata['rooms'])
    assert all(room['functions'] for room in building.metadata['rooms'])


@pytest.mark.parametrize('width,depth,height,pitch,ridge', [(11, 13, 7, 1.5, 'z'), (25, 23, 5, 1.0, 'x'), (15, 15, 6, 1.5, 'z')])
def test_dimension_and_pitch_boundaries(width, depth, height, pitch, ridge):
    design = Design('Boundary specimen', (Volume('home', width=width, depth=depth, levels=(Level(('living', 'kitchen'), height=height), Level(('sleeping', 'study'), attic=True)), roof=Roof(pitch=pitch, ridge=ridge, dormers=1)),))
    assert generate(design, 7).validate().valid


def test_diversity_is_structural_and_realized():
    signatures = set()
    geometries = set()
    for client in CLIENTS:
        design = importlib.import_module('examples.' + client).DESIGN
        a, b = (generate(design, seed) for seed in (0, 1))
        assert a.metadata['structural_signature'] != b.metadata['structural_signature'], client
        assert a.metadata['geometry_signature'] != b.metadata['geometry_signature'], client
        assert a.grid.blocks != b.grid.blocks
        signatures.update([a.metadata['structural_signature'], b.metadata['structural_signature']])
        geometries.update([a.metadata['geometry_signature'], b.metadata['geometry_signature']])
    assert len(signatures) == len(CLIENTS) * 2
    assert len(geometries) == len(CLIENTS) * 2


def test_water_posts_are_not_manor_overhangs():
    from examples.stilt_water_cabin import DESIGN as WATER
    from examples.stilted_manor import DESIGN as MANOR
    wet = generate(WATER, 0)
    dry = generate(MANOR, 0)
    assert wet.plan.masses[0].supports == 'posts'
    assert wet.plan.masses[0].levels[0].floor > WATER.site.water_depth
    assert dry.plan.masses[0].levels[0].rect.width < dry.plan.masses[0].levels[1].rect.width
    assert dry.plan.design.site.terrain != 'water'
    assert any(s['owner'] == 'manor/1' for s in dry.metadata['supports'])


def test_root_envelope_is_nonrectangular_and_buried_floor_excavated():
    from examples.root_house import DESIGN as ROOT
    from examples.sandstone_cottage import DESIGN as SLOPE
    root = generate(ROOT, 0)
    level = root.plan.masses[0].levels[0]
    assert len(level.cells) < level.rect.width * level.rect.depth
    assert any('dark_oak_log' in s and (x, z) not in level.cells for (x, y, z), s in root.grid.blocks.items())
    slope = generate(SLOPE, 0)
    assert slope.plan.design.site.terrain == 'slope'
    assert any('water' in state for state in slope.grid.blocks.values())
    assert slope.validate().valid


def test_structural_signatures_exclude_material_noise_and_furniture():
    a = Design('A', (Volume('home', width=13, depth=13),), materials=Materials(wall='stone', roof='slate'))
    b = replace(a, name='B', materials=Materials(wall='plaster', roof='red', accent='blue'))
    first, second = generate(a, 11), generate(b, 11)
    assert first.grid.blocks != second.grid.blocks
    assert first.metadata['structural_signature'] == second.metadata['structural_signature']
    assert first.metadata['geometry_signature'] == second.metadata['geometry_signature']


@pytest.mark.parametrize('side', ('north', 'south', 'east', 'west'))
@pytest.mark.parametrize('terrain', ('meadow', 'slope'))
def test_approach_meets_actual_terrain_in_every_direction(side, terrain):
    from hearthwright.materials import DIRECTIONS
    from hearthwright.navigation import walkable
    design = Design('Side entry', (Volume('home', width=13, depth=13),), site=Site(terrain=terrain, rise=7), entrance=Entrance('home', side))
    building = generate(design, 17)
    x, y, z = building.metadata['start']
    dx, dz = DIRECTIONS[side]
    assert walkable(building.grid, (x + dx, y, z + dz))
    assert building.validate().valid


@pytest.mark.parametrize('design', [
    Design('Four floors', (Volume('home', width=15, depth=17, levels=(Level(('workshop', 'storage'), height=7), Level(('living', 'kitchen'), height=6), Level(('sleeping', 'study')), Level(('sleeping', 'storage'), attic=True)), roof=Roof(ridge='z', pitch=1.5, eaves=2)),)),
    Design('Deep shallows', (Volume('home', width=13, depth=11, raised=5, supports='posts', roof=Roof(form='hip', pitch=.5, eaves=2)),), site=Site(terrain='water', water_depth=3), attachments=(Attachment('deck', 'home', side='west', depth=7),), entrance=Entrance('home', 'west')),
    Design('Hip loft', (Volume('home', width=15, depth=15, levels=(Level(('living', 'kitchen')), Level(('sleeping', 'study'), attic=True)), roof=Roof(form='hip', pitch=1, eaves=2)),)),
])
def test_dependent_domain_boundaries(design):
    assert generate(design, 17).validate().valid
