# Window-mounted planting with scoped sill replacement and actual masonry anchors.
from dataclasses import dataclass
from hearth import Box, ContractError
from hearth.kernel import Capability, Contract, Plan, Rule
from hearth.blocks import state_of


@dataclass(frozen=True)
class WindowBox:
    flowers: tuple[str, ...] = ('poppy', 'cornflower', 'oxeye_daisy')

    def capability(self):
        return Capability('garden.window-box', ('planter', 'plant', 'attachment'), assumptions=('window garden mounting interface',),
                          guarantees=('soil supports flowers', 'wall anchors', 'within negotiated facade projection'))

    def negotiate(self, ctx, parameters):
        if len(ctx.bindings) != 1:
            raise ContractError('window-box-host', ctx.path)
        port = ctx.view.port(ctx.bindings[0].host, ctx.bindings[0].port)
        if port.kind != 'window-garden' or port.frame != ctx.frame:
            raise ContractError('window-box-interface', ctx.path, ctx.frame.origin)
        if not self.flowers or any(f not in ('poppy', 'cornflower', 'oxeye_daisy', 'allium', 'azure_bluet') for f in self.flowers):
            raise ContractError('window-box-flower-domain', ctx.path)
        width = port.facts['width']
        framed = port.facts.get('depth', 1) == 2
        box = Box((-1 if framed else 0, 0, -1 if framed else 0), (width if framed else width - 1, 1, 0))
        if not all(port.region.contains(ctx.world(p)) for p in box.corners()):
            raise ContractError('window-box-region', ctx.path)
        boards = tuple((x, 0, -1) for x in range(width)) + ((-1, 0, 0), (width, 0, 0)) if framed else ()
        return Contract(box, rules=(Rule('support', tuple((x, 0, 1) for x in range(width)) + tuple((x, 0, 0) for x in range(width))),
                                    Rule('expected', tuple((x, 1, 0) for x in range(width)), data={'names': list(self.flowers)}),
                                    Rule('expected', boards, data={'names': ['spruce_trapdoor']})),
                        decisions={'width': width, 'framed': framed}, atomic_object=True)

    def realize(self, ctx, contract):
        p = Plan()
        rng = ctx.rng('flowers')
        for x in range(contract.decisions['width']):
            p.block((x, 0, 0), state_of('rooted_dirt'), 'planting-soil')
            p.block((x, 1, 0), state_of(rng.choice(self.flowers)), 'flower')
            if contract.decisions['framed']:
                p.block((x, 0, -1), state_of('spruce_trapdoor', facing='north', half='bottom', open='true', powered='false', waterlogged='false'), 'front-board')
        if contract.decisions['framed']:
            for x, side in ((-1, 'west'), (contract.decisions['width'], 'east')):
                p.block((x, 0, 0), state_of('spruce_trapdoor', facing=side, half='bottom', open='true', powered='false', waterlogged='false'), 'end-board')
        return p
