#!/usr/bin/env python3
"""Load + validate cp-criteria.yaml — the single source of truth for the scorer.

Failure to validate intent assertions = startup error, by design. The whole
point of the YAML is to make the philosophy auditable; silent drift defeats it.

Usage:
    from criteria_loader import CRITERIA
    weights = CRITERIA.weighted_criteria        # dict[str, dict]
    char_range = CRITERIA.deltas['character']   # {'range': [-0.5, 0.5], ...}
    if CRITERIA.gates['price']['max_purchase_eur'] < p.get('price', 0):
        ...
"""
from __future__ import annotations

import sys
from dataclasses import dataclass
from pathlib import Path

import yaml

YAML_PATH = (
    Path(__file__).resolve().parent.parent.parent.parent
    / 'system' / 'memory' / 'life' / 'cyber-prairie-criteria.yaml'
)


@dataclass
class Criteria:
    strategy: dict
    intent: list[str]
    gates: dict
    weighted_criteria: dict
    deltas: dict
    diamond_signals: dict
    keywords: dict
    vibe_regions: dict
    countries: dict


def _delta_span(delta_cfg: dict) -> float:
    lo, hi = delta_cfg['range']
    return hi - lo


def _validate_intent(c: Criteria) -> list[str]:
    """Return list of intent violations (empty = all pass)."""
    violations = []
    char = c.deltas.get('character', {})
    char_span = _delta_span(char) if char.get('range') else 0
    char_max = char.get('range', [0, 0])[1]

    # Intent: character is the single heaviest graded signal (max upside ≥ any other)
    for name, cfg in c.deltas.items():
        if name == 'character' or not cfg.get('range'):
            continue
        other_max = cfg['range'][1]
        if other_max > char_max:
            violations.append(
                f"INTENT VIOLATION: '{name}' upside +{other_max} exceeds character +{char_max} "
                f"(intent: 'character is the single heaviest graded signal')"
            )

    # Intent: revenue diversification rivals character — interpreted as ≥80% of character upside
    rev = c.deltas.get('revenue_viability', {})
    if rev.get('range') and rev['range'][1] < 0.80 * char_max:
        violations.append(
            f"INTENT VIOLATION: 'revenue_viability' upside +{rev['range'][1]} is <80% of character +{char_max} "
            "(intent: 'revenue diversification rivals character')"
        )

    # Intent: expandability ≥70% of character's UPSIDE (revealed preference 2026-05-30).
    # Compare upside-to-upside, not span-to-span: character has real downside for ugly
    # modern buildings; expandability only rewards presence — different shapes. The
    # 2026-05-28 session learning was that outbuildings appear in 7/8 top picks, which
    # is an upside signal. Span comparison would force a symmetric downside that the
    # research doesn't support.
    exp = c.deltas.get('expandability', {})
    if exp.get('range') and exp['range'][1] < 0.70 * char_max:
        violations.append(
            f"INTENT VIOLATION: expandability upside +{exp['range'][1]} is <70% of character +{char_max} "
            "(intent: 'expandability ≥70% of character's range')"
        )

    # Intent: views/setting weighted ≥3.0 (review-log lesson)
    loc_w = c.weighted_criteria.get('location_view', {}).get('weight', 0)
    if loc_w < 3.0:
        violations.append(
            f"INTENT VIOLATION: location_view weight {loc_w} below 3.0 floor "
            "(intent: 'views/setting weighted ≥3.0 — review-log property #3 lesson')"
        )

    # Intent: three Tier-1 review-log gates enforced
    for required_gate in ('privacy', 'road_noise', 'internet'):
        if required_gate not in c.gates:
            violations.append(
                f"INTENT VIOLATION: missing gate '{required_gate}' "
                "(intent: 'three Tier-1 review-log gates ENFORCED: privacy, road noise, internet')"
            )

    return violations


def load(yaml_path: Path | None = None) -> Criteria:
    p = yaml_path or YAML_PATH
    with open(p, encoding='utf-8') as f:
        data = yaml.safe_load(f)
    c = Criteria(
        strategy=data['strategy'],
        intent=data['intent'],
        gates=data['gates'],
        weighted_criteria=data['weighted_criteria'],
        deltas=data['deltas'],
        diamond_signals=data['diamond_signals'],
        keywords=data['keywords'],
        vibe_regions=data['vibe_regions'],
        countries=data['countries'],
    )
    violations = _validate_intent(c)
    if violations:
        msg = '\n'.join(['cp-criteria.yaml failed intent validation:', *violations])
        raise ValueError(msg)
    return c


# Eagerly load at import — startup error if YAML is broken.
CRITERIA = load()


if __name__ == '__main__':
    # CLI: print summary and intent-validation outcome
    print(f'Loaded: {YAML_PATH}')
    print(f'Strategy: {CRITERIA.strategy["name"]}')
    print(f'Intent assertions: {len(CRITERIA.intent)} (all passed)')
    print(f'Gates: {len(CRITERIA.gates)} categories')
    print(f'Weighted criteria: {len(CRITERIA.weighted_criteria)}')
    print(f'Post-weighting deltas: {len(CRITERIA.deltas)}')
    print(f'Diamond signals: {len([k for k in CRITERIA.diamond_signals if k != "cumulative_cap"])}')
    print(f'Keyword banks: {len(CRITERIA.keywords)}')
    print(f'Countries enabled: {[k for k,v in CRITERIA.countries.items() if v.get("enabled")]}')
    sys.exit(0)
