"""
Activity Metrics Placeholder Registrations

Registers 17 Aktivitäts-Platzhalter hier; 3 Session-/Erholungs-Keys in activity_session_insights.py (20 gesamt).

Evidence-based metadata with clear tagging of source.

Groups:
- Legacy Resolver (3): activity_summary, activity_detail, trainingstyp_verteilung
- Basic Metrics (7): training_minutes_week, training_frequency_7d, quality_sessions_pct,
                     proxy_internal_load_7d, monotony_score, strain_score, rest_day_compliance
- Advanced Metrics (7): ability_balance_*, vo2max_trend_28d, activity_score

Resolver: alle Keys gebündelt unter „Training / Aktivität“ in PLACEHOLDER_MAP;
activity_score nicht unter „Meta Scores“.
"""

from placeholder_registry import (
    PlaceholderMetadata,
    MissingValuePolicy,
    EvidenceType,
    OutputType,
    PlaceholderType,
    register_placeholder
)


# =============================================================================
# GRUPPE 1: Legacy Resolver (3 Placeholders)
# =============================================================================

def register_activity_group_1():
    """
    Register Group 1: Legacy Resolver placeholders.

    These use old resolver pattern (direct formatting in resolver, no data layer).
    """

    # ── activity_summary ──────────────────────────────────────────────────────

    activity_summary_metadata = PlaceholderMetadata(
        key="activity_summary",
        category="Aktivität",
        description="Zusammenfassung der letzten 14 Tage Aktivität",
        resolver_module="backend/placeholder_resolver.py",
        resolver_function="_format_activity_summary",
        data_layer_module=None,
        data_layer_function=None,
        source_tables=["activity_log", "training_types"],
        semantic_contract=(
            "Liefert eine kompakte textuelle Zusammenfassung der Trainingsaktivitäten "
            "der letzten 14 Tage. Beinhaltet: Anzahl Einheiten, Gesamtdauer, "
            "Trainingstypen-Verteilung (Top 3), durchschnittliche Dauer pro Einheit."
        ),
        business_meaning=(
            "Schneller Überblick für KI-Prompts über aktuelle Trainingsaktivität. "
            "Erlaubt Prompt-Autoren, Trainingsvolumen und -vielfalt auf einen Blick "
            "zu erfassen ohne einzelne Datenpunkte zu konsumieren."
        ),
        unit="text",
        time_window="14d",
        output_type=OutputType.TEXT_SUMMARY,
        placeholder_type=PlaceholderType.INTERPRETED,
        format_hint="Mehrere Sätze, menschenlesbar, Deutsch",
        example_output=(
            "14 Einheiten (315 min gesamt). Top-Typen: Krafttraining (5x, 180 min), "
            "Ausdauer (4x, 90 min), Mobilität (3x, 30 min). Ø Dauer: 22 min/Einheit."
        ),
        minimum_data_requirements=None,
        quality_filter_policy=None,
        confidence_logic=(
            "Keine formale Confidence-Berechnung. Verfügbarkeit = mindestens 1 "
            "activity_log Eintrag in 14d."
        ),
        missing_value_policy=MissingValuePolicy(
            available=False,
            value_raw=None,
            missing_reason="no_data",
            legacy_display="Keine Aktivitätsdaten"
        ),
        known_limitations=(
            "OLD RESOLVER PATTERN: Keine Data Layer Funktion. Formatierung direkt "
            "im Resolver. JOIN mit training_types für Typ-Namen. "
            "CRITICAL: Keine Qualitätsprüfung - auch 1-Minuten-Einheiten werden gezählt."
        ),
        layer_1_decision="NONE - Old resolver pattern (direct SQL in resolver)",
        layer_2a_decision="Placeholder Resolver (formatting + SQL query)",
        layer_2b_reuse_possible=False,
        architecture_alignment=(
            "NOT ALIGNED with Phase 0c Multi-Layer Architecture. "
            "Should be refactored to use data_layer function."
        ),
        issue_53_alignment="NOT ALIGNED - no layer separation"
    )

    activity_summary_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
    activity_summary_metadata.set_evidence("category", EvidenceType.CODE_DERIVED)
    activity_summary_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED)
    activity_summary_metadata.set_evidence("resolver_module", EvidenceType.CODE_DERIVED)
    activity_summary_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
    activity_summary_metadata.set_evidence("data_layer_module", EvidenceType.CODE_DERIVED)
    activity_summary_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED)
    activity_summary_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED)
    activity_summary_metadata.set_evidence("semantic_contract", EvidenceType.MIXED)
    activity_summary_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
    activity_summary_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED)
    activity_summary_metadata.set_evidence("time_window", EvidenceType.CODE_DERIVED)
    activity_summary_metadata.set_evidence("output_type", EvidenceType.CODE_DERIVED)
    activity_summary_metadata.set_evidence("placeholder_type", EvidenceType.MIXED)
    activity_summary_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED)
    activity_summary_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
    activity_summary_metadata.set_evidence("minimum_data_requirements", EvidenceType.UNRESOLVED)
    activity_summary_metadata.set_evidence("quality_filter_policy", EvidenceType.UNRESOLVED)
    activity_summary_metadata.set_evidence("confidence_logic", EvidenceType.CODE_DERIVED)
    activity_summary_metadata.set_evidence("missing_value_policy", EvidenceType.CODE_DERIVED)
    activity_summary_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED)
    activity_summary_metadata.set_evidence("layer_1_decision", EvidenceType.CODE_DERIVED)
    activity_summary_metadata.set_evidence("layer_2a_decision", EvidenceType.CODE_DERIVED)
    activity_summary_metadata.set_evidence("layer_2b_reuse_possible", EvidenceType.CODE_DERIVED)
    activity_summary_metadata.set_evidence("architecture_alignment", EvidenceType.CODE_DERIVED)
    activity_summary_metadata.set_evidence("issue_53_alignment", EvidenceType.CODE_DERIVED)

    register_placeholder(activity_summary_metadata)

    # ── activity_detail ───────────────────────────────────────────────────────

    activity_detail_metadata = PlaceholderMetadata(
        key="activity_detail",
        category="Aktivität",
        description="Detaillierte Liste der letzten 14 Tage Aktivität",
        resolver_module="backend/placeholder_resolver.py",
        resolver_function="_format_activity_detail",
        data_layer_module=None,
        data_layer_function=None,
        source_tables=["activity_log", "training_types"],
        semantic_contract=(
            "Liefert eine strukturierte Liste aller Trainingseinheiten der letzten 14 Tage. "
            "Jede Einheit: Datum, Trainingstyp, Dauer (Minuten), optional Notizen. "
            "Sortiert chronologisch absteigend (neueste zuerst)."
        ),
        business_meaning=(
            "Detaillierte Trainingshistorie für KI-Prompts, die Muster, Progressionen "
            "oder Abweichungen analysieren sollen. Erlaubt Erkennung von Trainingszyklen, "
            "Pausentagen, Intensitätsmustern."
        ),
        unit="list",
        time_window="14d",
        output_type=OutputType.LIST,
        placeholder_type=PlaceholderType.RAW_DATA,
        format_hint="Liste von Strings, eine Zeile pro Einheit: 'YYYY-MM-DD: Typ (Dauer min)'",
        example_output=(
            "2026-03-28: Krafttraining (45 min)\\n"
            "2026-03-27: Laufen (30 min)\\n"
            "2026-03-25: Yoga (20 min)"
        ),
        minimum_data_requirements=None,
        quality_filter_policy=None,
        confidence_logic="Keine Confidence-Berechnung. Rohdaten-Liste.",
        missing_value_policy=MissingValuePolicy(
            available=False,
            value_raw=None,
            missing_reason="no_data",
            legacy_display="Keine Aktivitätsdaten"
        ),
        known_limitations=(
            "OLD RESOLVER PATTERN: Keine Data Layer Funktion. "
            "Formatierung direkt im Resolver. "
            "CRITICAL: Keine Qualitätsfilterung - auch ungültige Einheiten (z.B. 0 min) "
            "werden gelistet. JOIN mit training_types für Typ-Namen."
        ),
        layer_1_decision="NONE - Old resolver pattern (direct SQL in resolver)",
        layer_2a_decision="Placeholder Resolver (formatting + SQL query)",
        layer_2b_reuse_possible=False,
        architecture_alignment=(
            "NOT ALIGNED with Phase 0c Multi-Layer Architecture. "
            "Should be refactored to use data_layer function."
        ),
        issue_53_alignment="NOT ALIGNED - no layer separation"
    )

    activity_detail_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
    activity_detail_metadata.set_evidence("category", EvidenceType.CODE_DERIVED)
    activity_detail_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED)
    activity_detail_metadata.set_evidence("resolver_module", EvidenceType.CODE_DERIVED)
    activity_detail_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
    activity_detail_metadata.set_evidence("data_layer_module", EvidenceType.CODE_DERIVED)
    activity_detail_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED)
    activity_detail_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED)
    activity_detail_metadata.set_evidence("semantic_contract", EvidenceType.MIXED)
    activity_detail_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
    activity_detail_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED)
    activity_detail_metadata.set_evidence("time_window", EvidenceType.CODE_DERIVED)
    activity_detail_metadata.set_evidence("output_type", EvidenceType.CODE_DERIVED)
    activity_detail_metadata.set_evidence("placeholder_type", EvidenceType.MIXED)
    activity_detail_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED)
    activity_detail_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
    activity_detail_metadata.set_evidence("minimum_data_requirements", EvidenceType.UNRESOLVED)
    activity_detail_metadata.set_evidence("quality_filter_policy", EvidenceType.UNRESOLVED)
    activity_detail_metadata.set_evidence("confidence_logic", EvidenceType.CODE_DERIVED)
    activity_detail_metadata.set_evidence("missing_value_policy", EvidenceType.CODE_DERIVED)
    activity_detail_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED)
    activity_detail_metadata.set_evidence("layer_1_decision", EvidenceType.CODE_DERIVED)
    activity_detail_metadata.set_evidence("layer_2a_decision", EvidenceType.CODE_DERIVED)
    activity_detail_metadata.set_evidence("layer_2b_reuse_possible", EvidenceType.CODE_DERIVED)
    activity_detail_metadata.set_evidence("architecture_alignment", EvidenceType.CODE_DERIVED)
    activity_detail_metadata.set_evidence("issue_53_alignment", EvidenceType.CODE_DERIVED)

    register_placeholder(activity_detail_metadata)

    # ── trainingstyp_verteilung ───────────────────────────────────────────────

    trainingstyp_verteilung_metadata = PlaceholderMetadata(
        key="trainingstyp_verteilung",
        category="Aktivität",
        description="Trainingstypen-Verteilung der letzten 14 Tage als JSON",
        resolver_module="backend/placeholder_resolver.py",
        resolver_function="_format_trainingstyp_verteilung",
        data_layer_module=None,
        data_layer_function=None,
        source_tables=["activity_log", "training_types"],
        semantic_contract=(
            "Liefert eine JSON-Struktur mit der Verteilung der Trainingstypen über 14 Tage. "
            "Für jeden Trainingstyp: Anzahl Einheiten, Gesamtdauer (Minuten), "
            "Prozentanteil an Gesamtdauer. Sortiert nach Dauer absteigend."
        ),
        business_meaning=(
            "Analyse-Placeholder für Trainingsvielfalt und -schwerpunkte. "
            "Erlaubt KI-Prompts, Imbalancen zu erkennen (z.B. nur Kraft, keine Ausdauer) "
            "oder Zielkonformität zu prüfen (z.B. 'zu wenig Mobilität')."
        ),
        unit="json",
        time_window="14d",
        output_type=OutputType.JSON,
        placeholder_type=PlaceholderType.INTERPRETED,
        format_hint="JSON Object mit Trainingstyp als Key, Value: {count, duration_min, percentage}",
        example_output=(
            '{"Krafttraining": {"count": 5, "duration_min": 180, "percentage": 57}, '
            '"Ausdauer": {"count": 4, "duration_min": 90, "percentage": 29}, '
            '"Mobilität": {"count": 3, "duration_min": 45, "percentage": 14}}'
        ),
        minimum_data_requirements=None,
        quality_filter_policy=None,
        confidence_logic="Keine Confidence-Berechnung. Aggregation basiert auf verfügbaren Daten.",
        missing_value_policy=MissingValuePolicy(
            available=False,
            value_raw=None,
            missing_reason="no_data",
            legacy_display="{}"
        ),
        known_limitations=(
            "OLD RESOLVER PATTERN: Keine Data Layer Funktion. "
            "Aggregation direkt im Resolver. "
            "CRITICAL: Keine Qualitätsfilterung - auch ungültige Einheiten werden aggregiert. "
            "JOIN mit training_types für Typ-Namen. "
            "EDGE CASE: Einheiten ohne training_type_id werden ignoriert (LEFT JOIN)."
        ),
        layer_1_decision="NONE - Old resolver pattern (direct SQL aggregation in resolver)",
        layer_2a_decision="Placeholder Resolver (aggregation + JSON formatting)",
        layer_2b_reuse_possible=True,
        architecture_alignment=(
            "PARTIALLY ALIGNED: JSON output structure suitable for chart endpoints, "
            "but no data layer separation. Should be refactored."
        ),
        issue_53_alignment="PARTIALLY ALIGNED - output format good, layer separation missing"
    )

    trainingstyp_verteilung_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
    trainingstyp_verteilung_metadata.set_evidence("category", EvidenceType.CODE_DERIVED)
    trainingstyp_verteilung_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED)
    trainingstyp_verteilung_metadata.set_evidence("resolver_module", EvidenceType.CODE_DERIVED)
    trainingstyp_verteilung_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
    trainingstyp_verteilung_metadata.set_evidence("data_layer_module", EvidenceType.CODE_DERIVED)
    trainingstyp_verteilung_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED)
    trainingstyp_verteilung_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED)
    trainingstyp_verteilung_metadata.set_evidence("semantic_contract", EvidenceType.MIXED)
    trainingstyp_verteilung_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
    trainingstyp_verteilung_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED)
    trainingstyp_verteilung_metadata.set_evidence("time_window", EvidenceType.CODE_DERIVED)
    trainingstyp_verteilung_metadata.set_evidence("output_type", EvidenceType.CODE_DERIVED)
    trainingstyp_verteilung_metadata.set_evidence("placeholder_type", EvidenceType.MIXED)
    trainingstyp_verteilung_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED)
    trainingstyp_verteilung_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
    trainingstyp_verteilung_metadata.set_evidence("minimum_data_requirements", EvidenceType.UNRESOLVED)
    trainingstyp_verteilung_metadata.set_evidence("quality_filter_policy", EvidenceType.UNRESOLVED)
    trainingstyp_verteilung_metadata.set_evidence("confidence_logic", EvidenceType.CODE_DERIVED)
    trainingstyp_verteilung_metadata.set_evidence("missing_value_policy", EvidenceType.CODE_DERIVED)
    trainingstyp_verteilung_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED)
    trainingstyp_verteilung_metadata.set_evidence("layer_1_decision", EvidenceType.CODE_DERIVED)
    trainingstyp_verteilung_metadata.set_evidence("layer_2a_decision", EvidenceType.CODE_DERIVED)
    trainingstyp_verteilung_metadata.set_evidence("layer_2b_reuse_possible", EvidenceType.TO_VERIFY)
    trainingstyp_verteilung_metadata.set_evidence("architecture_alignment", EvidenceType.MIXED)
    trainingstyp_verteilung_metadata.set_evidence("issue_53_alignment", EvidenceType.MIXED)

    register_placeholder(trainingstyp_verteilung_metadata)


# =============================================================================
# GRUPPE 2: Basic Metrics (7 Placeholders)
# =============================================================================

def register_activity_group_2():
    """
    Register Group 2: Basic Activity Metrics.

    All use Phase 0c Data Layer architecture.
    """

    # Common metadata for most Group 2 placeholders
    common_metadata_7d = {
        "category": "Aktivität",
        "resolver_module": "backend/placeholder_resolver.py",
        "data_layer_module": "backend/data_layer/activity_metrics.py",
        "source_tables": ["activity_log"],
        "time_window": "7d",
        "output_type": OutputType.NUMERIC,
        "placeholder_type": PlaceholderType.INTERPRETED,
        "confidence_logic": "datenpunktbasierte Coverage-Logik (calculate_confidence in data layer)",
        "missing_value_policy": MissingValuePolicy(
            available=False,
            value_raw=None,
            missing_reason="insufficient_data",
            legacy_display="nicht genug Daten"
        ),
        "layer_1_decision": "Data Layer (activity_metrics.py)",
        "layer_2a_decision": "Placeholder Resolver (formatting only)",
        "architecture_alignment": "Phase 0c Multi-Layer Architecture conform",
        "issue_53_alignment": "Layer separation established"
    }

    # Common evidence for Group 2 placeholders
    common_evidence_7d = {
        "category": EvidenceType.CODE_DERIVED,
        "resolver_module": EvidenceType.CODE_DERIVED,
        "data_layer_module": EvidenceType.CODE_DERIVED,
        "source_tables": EvidenceType.CODE_DERIVED,
        "time_window": EvidenceType.CODE_DERIVED,
        "output_type": EvidenceType.CODE_DERIVED,
        "placeholder_type": EvidenceType.MIXED,
        "confidence_logic": EvidenceType.CODE_DERIVED,
        "missing_value_policy": EvidenceType.CODE_DERIVED,
        "layer_1_decision": EvidenceType.CODE_DERIVED,
        "layer_2a_decision": EvidenceType.CODE_DERIVED,
        "layer_2b_reuse_possible": EvidenceType.TO_VERIFY,
        "architecture_alignment": EvidenceType.CODE_DERIVED,
        "issue_53_alignment": EvidenceType.CODE_DERIVED,
        "minimum_data_requirements": EvidenceType.UNRESOLVED,
        "quality_filter_policy": EvidenceType.UNRESOLVED
    }

    # ── training_minutes_week ─────────────────────────────────────────────────

    training_minutes_week_metadata = PlaceholderMetadata(
        key="training_minutes_week",
        description="Gesamte Trainingsminuten in 7 Tagen",
        resolver_function="get_training_minutes_week",
        data_layer_function="get_weekly_training_volume",
        semantic_contract=(
            "Liefert die Summe aller Trainingsminuten über die letzten 7 Tage. "
            "Basiert auf activity_log.duration_minutes. Keine Qualitätsfilterung - "
            "alle Einheiten werden summiert unabhängig von Dauer oder Intensität."
        ),
        business_meaning=(
            "Kernmetrik für Trainingsvolumen und Periodisierung. "
            "Erlaubt Erkennung von Übertraining (zu hoch), Detraining (zu niedrig), "
            "und Trendanalysen (Volumen steigend/fallend)."
        ),
        unit="Minuten",
        format_hint="Ganzzahl",
        example_output="315",
        known_limitations=(
            "Keine Qualitätsfilterung. 1-Minuten-Einheiten zählen gleich wie "
            "60-Minuten-Sessions. Keine Intensitätsgewichtung. "
            "EDGE CASE: duration_minutes = 0 oder NULL werden als 0 gezählt."
        ),
        layer_2b_reuse_possible=True,
        **common_metadata_7d
    )

    training_minutes_week_metadata.evidence.update(common_evidence_7d)
    training_minutes_week_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
    training_minutes_week_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED)
    training_minutes_week_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
    training_minutes_week_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED)
    training_minutes_week_metadata.set_evidence("semantic_contract", EvidenceType.MIXED)
    training_minutes_week_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
    training_minutes_week_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED)
    training_minutes_week_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED)
    training_minutes_week_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
    training_minutes_week_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED)

    register_placeholder(training_minutes_week_metadata)

    # ── training_frequency_7d ─────────────────────────────────────────────────

    training_frequency_7d_metadata = PlaceholderMetadata(
        key="training_frequency_7d",
        description="Anzahl Trainingseinheiten in 7 Tagen",
        resolver_function="get_training_frequency_7d",
        data_layer_function="get_training_frequency",
        semantic_contract=(
            "Liefert die Anzahl der Trainingseinheiten über die letzten 7 Tage. "
            "Basiert auf COUNT(activity_log.id). Keine Qualitätsfilterung - "
            "jede Einheit wird gezählt unabhängig von Dauer oder Intensität."
        ),
        business_meaning=(
            "Frequenz-Metrik für Trainingsregelmäßigkeit. "
            "Erlaubt Erkennung von Konsistenz (z.B. 4-5 Einheiten/Woche), "
            "Pausentagen, und Trainingszyklen."
        ),
        unit="Anzahl Einheiten",
        format_hint="Ganzzahl",
        example_output="5",
        known_limitations=(
            "Keine Qualitätsfilterung. 1-Minuten-Einheiten zählen gleich wie "
            "60-Minuten-Sessions. "
            "EDGE CASE: Mehrere Einheiten am selben Tag werden separat gezählt "
            "(kein Tages-Grouping)."
        ),
        layer_2b_reuse_possible=True,
        **common_metadata_7d
    )

    training_frequency_7d_metadata.evidence.update(common_evidence_7d)
    training_frequency_7d_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
    training_frequency_7d_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED)
    training_frequency_7d_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
    training_frequency_7d_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED)
    training_frequency_7d_metadata.set_evidence("semantic_contract", EvidenceType.MIXED)
    training_frequency_7d_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
    training_frequency_7d_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED)
    training_frequency_7d_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED)
    training_frequency_7d_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
    training_frequency_7d_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED)

    register_placeholder(training_frequency_7d_metadata)

    # ── quality_sessions_pct ──────────────────────────────────────────────────

    # Custom metadata for quality_sessions_pct (28d time window, different source tables)
    quality_metadata_custom = {**common_metadata_7d}
    quality_metadata_custom["time_window"] = "28d"
    quality_metadata_custom["source_tables"] = ["activity_log", "training_types"]

    quality_sessions_pct_metadata = PlaceholderMetadata(
        key="quality_sessions_pct",
        description="Prozentsatz Qualitäts-Sessions in 28 Tagen",
        resolver_function="get_quality_sessions_pct",
        data_layer_function="calculate_quality_sessions_percentage",
        semantic_contract=(
            "Liefert den Prozentsatz der Trainingseinheiten, die als 'Qualitäts-Sessions' "
            "klassifiziert werden (duration >= 30 min UND intensity >= moderate). "
            "Berechnung: (quality_sessions / total_sessions) × 100."
        ),
        business_meaning=(
            "Qualitäts-Metrik für Trainingseffektivität. "
            "Niedrige Werte (<50%) deuten auf zu viele kurze/low-intensity Sessions. "
            "Hohe Werte (>80%) deuten auf konsequent forderndes Training."
        ),
        unit="Prozent",
        format_hint="Ganzzahl 0-100",
        example_output="68",
        known_limitations=(
            "CRITICAL TO_VERIFY: Code nutzt quality_label Feld (may not exist). "
            "Canonical expects duration >= 30min + intensity >= moderate. "
            "MISMATCH: quality_label vs. calculated quality. "
            "EDGE CASE: Einheiten ohne training_type_id werden ignoriert."
        ),
        layer_2b_reuse_possible=True,
        **quality_metadata_custom
    )

    quality_sessions_pct_metadata.evidence.update(common_evidence_7d)
    quality_sessions_pct_metadata.evidence["time_window"] = EvidenceType.CODE_DERIVED
    quality_sessions_pct_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
    quality_sessions_pct_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED)
    quality_sessions_pct_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
    quality_sessions_pct_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED)
    quality_sessions_pct_metadata.set_evidence("semantic_contract", EvidenceType.TO_VERIFY)
    quality_sessions_pct_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
    quality_sessions_pct_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED)
    quality_sessions_pct_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED)
    quality_sessions_pct_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
    quality_sessions_pct_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED)
    quality_sessions_pct_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED)

    register_placeholder(quality_sessions_pct_metadata)

    # ── proxy_internal_load_7d ────────────────────────────────────────────────

    # Custom metadata for proxy_internal_load (different source tables)
    proxy_load_metadata_custom = {**common_metadata_7d}
    proxy_load_metadata_custom["source_tables"] = ["activity_log", "training_types"]

    proxy_internal_load_7d_metadata = PlaceholderMetadata(
        key="proxy_internal_load_7d",
        description="Proxy Internal Load über 7 Tage",
        resolver_function="get_proxy_internal_load_7d",
        data_layer_function="calculate_proxy_internal_load",
        semantic_contract=(
            "Liefert einen Proxy-Wert für die interne Trainingsbelastung über 7 Tage. "
            "Berechnet als: Summe(duration × intensity_factor × quality_factor). "
            "Intensity_factor basiert auf RPE-Mapping (1-10 → 0.1-1.0). "
            "Quality_factor basiert auf quality_label (easy/moderate/hard → 0.8/1.0/1.2)."
        ),
        business_meaning=(
            "Load-Monitoring-Metrik für Periodisierung und Übertrainings-Prävention. "
            "Erlaubt Vergleich interner Belastung über Zeit (nicht nur Volumen). "
            "Basis für Monotony und Strain Scores."
        ),
        unit="Load-Punkte",
        format_hint="Ganzzahl",
        example_output="1850",
        known_limitations=(
            "PROXY-WERT: Nicht trainings wissenschaftlich validiert. "
            "RPE-Mapping vereinfacht (linear 1-10 → 0.1-1.0). "
            "CRITICAL BUG: RPE 4-5 maps to 'moderate' quality but 'moderate' "
            "not in quality_factors dict (easy/hard only). "
            "EDGE CASE: Einheiten ohne RPE bekommen intensity_factor = 0.5 (default)."
        ),
        layer_2b_reuse_possible=True,
        **proxy_load_metadata_custom
    )

    proxy_internal_load_7d_metadata.evidence.update(common_evidence_7d)
    proxy_internal_load_7d_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
    proxy_internal_load_7d_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED)
    proxy_internal_load_7d_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
    proxy_internal_load_7d_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED)
    proxy_internal_load_7d_metadata.set_evidence("semantic_contract", EvidenceType.CODE_DERIVED)
    proxy_internal_load_7d_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
    proxy_internal_load_7d_metadata.set_evidence("unit", EvidenceType.DRAFT_DERIVED)
    proxy_internal_load_7d_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED)
    proxy_internal_load_7d_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
    proxy_internal_load_7d_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED)
    proxy_internal_load_7d_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED)

    register_placeholder(proxy_internal_load_7d_metadata)

    # ── monotony_score ────────────────────────────────────────────────────────

    monotony_score_metadata = PlaceholderMetadata(
        key="monotony_score",
        description="Trainings-Monotonie-Score über 7 Tage",
        resolver_function="get_monotony_score",
        data_layer_function="calculate_monotony_score",
        semantic_contract=(
            "Liefert einen Monotonie-Score für die Trainingsbelastung über 7 Tage. "
            "Berechnet als: mean(daily_duration) / std_dev(daily_duration). "
            "Hohe Werte (>2.0) = monotones Training (gleichbleibende Belastung). "
            "Niedrige Werte (<1.5) = variable Belastung."
        ),
        business_meaning=(
            "Variabilitäts-Metrik für Periodisierung. "
            "Hohe Monotonie erhöht Übertrainings-Risiko (Foster et al. 2001). "
            "Kombination mit Strain Score für Übertrainings-Warnung."
        ),
        unit="Score (dimensionslos)",
        format_hint="Dezimalzahl, typisch 0.5-3.0",
        example_output="1.8",
        known_limitations=(
            "FORMULA: mean / std_dev. "
            "EDGE CASE: std_dev = 0 (alle Tage gleich) → division by zero → return mean as fallback. "
            "EDGE CASE: < 3 Trainingstage → nicht aussagekräftig, aber wird trotzdem berechnet. "
            "ZEITFENSTER: 7 Tage möglicherweise zu kurz für verlässliche Monotonie-Analyse "
            "(Foster original: 28 Tage)."
        ),
        layer_2b_reuse_possible=True,
        **common_metadata_7d
    )

    monotony_score_metadata.evidence.update(common_evidence_7d)
    monotony_score_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
    monotony_score_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED)
    monotony_score_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
    monotony_score_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED)
    monotony_score_metadata.set_evidence("semantic_contract", EvidenceType.CODE_DERIVED)
    monotony_score_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
    monotony_score_metadata.set_evidence("unit", EvidenceType.DRAFT_DERIVED)
    monotony_score_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED)
    monotony_score_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
    monotony_score_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED)

    register_placeholder(monotony_score_metadata)

    # ── strain_score ──────────────────────────────────────────────────────────

    strain_score_metadata = PlaceholderMetadata(
        key="strain_score",
        description="Trainings-Strain-Score über 7 Tage",
        resolver_function="get_strain_score",
        data_layer_function="calculate_strain_score",
        semantic_contract=(
            "Liefert einen Strain-Score für die Gesamtbelastung über 7 Tage. "
            "Berechnet als: proxy_internal_load_7d × monotony_score. "
            "Kombiniert Volumen (Load) mit Variabilität (Monotonie) zu Gesamt-Strain."
        ),
        business_meaning=(
            "Übertrainings-Risiko-Metrik (Foster et al. 2001). "
            "Hohe Werte = hohes Risiko (hohe Last + monotones Training). "
            "Schwellenwerte: <5000 = moderat, 5000-8000 = erhöht, >8000 = kritisch."
        ),
        unit="Strain-Punkte",
        format_hint="Ganzzahl, typisch 1000-10000",
        example_output="3330",
        known_limitations=(
            "FORMULA: load × monotony. "
            "ABHÄNGIG von proxy_internal_load_7d (bereits Proxy-Wert). "
            "SCHWELLENWERTE: Nicht wissenschaftlich validiert für diese App. "
            "EDGE CASE: Wenn monotony_score Fallback-Logik greift (std_dev=0), "
            "wird Strain = Load × mean (nicht original Formel)."
        ),
        layer_2b_reuse_possible=True,
        **common_metadata_7d
    )

    strain_score_metadata.evidence.update(common_evidence_7d)
    strain_score_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
    strain_score_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED)
    strain_score_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
    strain_score_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED)
    strain_score_metadata.set_evidence("semantic_contract", EvidenceType.CODE_DERIVED)
    strain_score_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
    strain_score_metadata.set_evidence("unit", EvidenceType.DRAFT_DERIVED)
    strain_score_metadata.set_evidence("format_hint", EvidenceType.DRAFT_DERIVED)
    strain_score_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
    strain_score_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED)

    register_placeholder(strain_score_metadata)

    # ── rest_day_compliance ───────────────────────────────────────────────────

    # Custom metadata for rest_day_compliance (28d time window, different source tables)
    rest_metadata_custom = {**common_metadata_7d}
    rest_metadata_custom["time_window"] = "28d"
    rest_metadata_custom["source_tables"] = ["rest_days", "activity_log"]

    rest_day_compliance_metadata = PlaceholderMetadata(
        key="rest_day_compliance",
        description="Ruhetag-Compliance über 28 Tage",
        resolver_function="get_rest_day_compliance",
        data_layer_function="calculate_rest_day_compliance",
        semantic_contract=(
            "Liefert den Prozentsatz der geplanten Ruhetage, die tatsächlich eingehalten wurden. "
            "Berechnung: (eingehaltene_ruhetage / geplante_ruhetage) × 100. "
            "Ruhetag = Eintrag in rest_days OHNE korrespondierenden activity_log Eintrag am selben Tag."
        ),
        business_meaning=(
            "Regenerations-Compliance-Metrik. "
            "Niedrige Werte (<70%) = schlechte Regeneration, Übertrainings-Risiko. "
            "Hohe Werte (>90%) = gute Disziplin bei Regeneration."
        ),
        unit="Prozent",
        format_hint="Ganzzahl 0-100",
        example_output="85",
        known_limitations=(
            "JSONB DEPENDENCY: Nutzt rest_config->>'focus' JSONB Feld. "
            "EDGE CASE: rest_config NULL → Ruhetag wird ignoriert (nicht gezählt). "
            "EDGE CASE: Kein activity_log Eintrag am Ruhetag = compliant (korrekt). "
            "EDGE CASE: Mehrere activity_log Einträge am Ruhetag = alle zählen als violation."
        ),
        layer_2b_reuse_possible=False,
        **rest_metadata_custom
    )

    rest_day_compliance_metadata.evidence.update(common_evidence_7d)
    rest_day_compliance_metadata.evidence["time_window"] = EvidenceType.CODE_DERIVED
    rest_day_compliance_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
    rest_day_compliance_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED)
    rest_day_compliance_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
    rest_day_compliance_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED)
    rest_day_compliance_metadata.set_evidence("semantic_contract", EvidenceType.MIXED)
    rest_day_compliance_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
    rest_day_compliance_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED)
    rest_day_compliance_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED)
    rest_day_compliance_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
    rest_day_compliance_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED)
    rest_day_compliance_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED)
    rest_day_compliance_metadata.set_evidence("layer_2b_reuse_possible", EvidenceType.CODE_DERIVED)

    register_placeholder(rest_day_compliance_metadata)


# =============================================================================
# GRUPPE 3: Advanced Metrics (7 Placeholders)
# =============================================================================

def register_activity_group_3():
    """
    Register Group 3: Advanced Activity Metrics.

    Includes ability balance metrics (5), VO2 Max trend, and activity score.
    """

    # Common metadata for ability_balance placeholders
    common_metadata_ability = {
        "category": "Aktivität",
        "resolver_module": "backend/placeholder_resolver.py",
        "data_layer_module": "backend/data_layer/activity_metrics.py",
        "data_layer_function": "calculate_ability_balance",
        "source_tables": ["activity_log", "training_types"],
        "time_window": "28d",
        "output_type": OutputType.NUMERIC,
        "placeholder_type": PlaceholderType.SCORE,
        "unit": "Score (0-100)",
        "format_hint": "Ganzzahl 0-100",
        "confidence_logic": "datenpunktbasierte Coverage-Logik (calculate_confidence in data layer)",
        "missing_value_policy": MissingValuePolicy(
            available=False,
            value_raw=None,
            missing_reason="insufficient_data",
            legacy_display="nicht genug Daten"
        ),
        "layer_1_decision": "Data Layer (activity_metrics.calculate_ability_balance)",
        "layer_2a_decision": "Placeholder Resolver (extract specific ability from dict)",
        "architecture_alignment": "Phase 0c Multi-Layer Architecture conform",
        "issue_53_alignment": "Layer separation established"
    }

    # Common evidence for ability_balance placeholders
    common_evidence_ability = {
        "category": EvidenceType.CODE_DERIVED,
        "resolver_module": EvidenceType.CODE_DERIVED,
        "data_layer_module": EvidenceType.CODE_DERIVED,
        "data_layer_function": EvidenceType.CODE_DERIVED,
        "source_tables": EvidenceType.CODE_DERIVED,
        "time_window": EvidenceType.CODE_DERIVED,
        "output_type": EvidenceType.CODE_DERIVED,
        "placeholder_type": EvidenceType.MIXED,
        "unit": EvidenceType.CODE_DERIVED,
        "format_hint": EvidenceType.CODE_DERIVED,
        "confidence_logic": EvidenceType.CODE_DERIVED,
        "missing_value_policy": EvidenceType.CODE_DERIVED,
        "layer_1_decision": EvidenceType.CODE_DERIVED,
        "layer_2a_decision": EvidenceType.CODE_DERIVED,
        "layer_2b_reuse_possible": EvidenceType.TO_VERIFY,
        "architecture_alignment": EvidenceType.CODE_DERIVED,
        "issue_53_alignment": EvidenceType.CODE_DERIVED,
        "minimum_data_requirements": EvidenceType.UNRESOLVED,
        "quality_filter_policy": EvidenceType.UNRESOLVED
    }

    # ── ability_balance_strength ──────────────────────────────────────────────

    ability_balance_strength_metadata = PlaceholderMetadata(
        key="ability_balance_strength",
        description="Fähigkeiten-Balance-Score: Kraft (28d)",
        resolver_function="get_ability_balance_strength",
        semantic_contract=(
            "Liefert einen normierten Score (0-100) für die relative Trainingsbelastung "
            "der Fähigkeit 'Kraft' über 28 Tage. Berechnung: "
            "(kraft_load / max_load_aller_fähigkeiten) × 100. "
            "Basiert auf training_types.abilities JSONB Feld (strength_percentage)."
        ),
        business_meaning=(
            "Balance-Metrik für Trainingsvielfalt. "
            "Zeigt ob Kraft überproportional (>80), balanciert (40-60), oder unterrepräsentiert (<20) trainiert wird. "
            "Vergleich mit anderen Fähigkeiten zeigt Imbalancen."
        ),
        example_output="75",
        known_limitations=(
            "JSONB DEPENDENCY: training_types.abilities JSONB Feld muss populated sein. "
            "FORMULA: max-normalized (nicht prozentual vom Total). "
            "Höchste Fähigkeit = 100, andere relativ dazu. "
            "EDGE CASE: abilities NULL → Trainingstyp wird mit 0% für alle Fähigkeiten gewertet. "
            "EDGE CASE: Alle Fähigkeiten = 0 (keine Trainings) → division by zero → return 0."
        ),
        layer_2b_reuse_possible=True,
        **common_metadata_ability
    )

    ability_balance_strength_metadata.evidence.update(common_evidence_ability)
    ability_balance_strength_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
    ability_balance_strength_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED)
    ability_balance_strength_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
    ability_balance_strength_metadata.set_evidence("semantic_contract", EvidenceType.CODE_DERIVED)
    ability_balance_strength_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
    ability_balance_strength_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
    ability_balance_strength_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED)

    register_placeholder(ability_balance_strength_metadata)

    # ── ability_balance_endurance ─────────────────────────────────────────────

    ability_balance_endurance_metadata = PlaceholderMetadata(
        key="ability_balance_endurance",
        description="Fähigkeiten-Balance-Score: Ausdauer (28d)",
        resolver_function="get_ability_balance_endurance",
        semantic_contract=(
            "Liefert einen normierten Score (0-100) für die relative Trainingsbelastung "
            "der Fähigkeit 'Ausdauer' über 28 Tage. Berechnung: "
            "(ausdauer_load / max_load_aller_fähigkeiten) × 100. "
            "Basiert auf training_types.abilities JSONB Feld (endurance_percentage)."
        ),
        business_meaning=(
            "Balance-Metrik für Trainingsvielfalt. "
            "Zeigt ob Ausdauer überproportional (>80), balanciert (40-60), oder unterrepräsentiert (<20) trainiert wird."
        ),
        example_output="92",
        known_limitations=(
            "JSONB DEPENDENCY: training_types.abilities JSONB Feld muss populated sein. "
            "FORMULA: max-normalized (nicht prozentual vom Total). "
            "EDGE CASE: abilities NULL → 0% Ausdauer. "
            "EDGE CASE: Alle Fähigkeiten = 0 → return 0."
        ),
        layer_2b_reuse_possible=True,
        **common_metadata_ability
    )

    ability_balance_endurance_metadata.evidence.update(common_evidence_ability)
    ability_balance_endurance_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
    ability_balance_endurance_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED)
    ability_balance_endurance_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
    ability_balance_endurance_metadata.set_evidence("semantic_contract", EvidenceType.CODE_DERIVED)
    ability_balance_endurance_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
    ability_balance_endurance_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
    ability_balance_endurance_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED)

    register_placeholder(ability_balance_endurance_metadata)

    # ── ability_balance_mental ────────────────────────────────────────────────

    ability_balance_mental_metadata = PlaceholderMetadata(
        key="ability_balance_mental",
        description="Fähigkeiten-Balance-Score: Geist & Meditation (28d)",
        resolver_function="get_ability_balance_mental",
        semantic_contract=(
            "Liefert einen normierten Score (0-100) für die relative Trainingsbelastung "
            "der Fähigkeit 'Geist & Meditation' über 28 Tage. Berechnung: "
            "(mental_load / max_load_aller_fähigkeiten) × 100. "
            "Basiert auf training_types.abilities JSONB Feld (mental_percentage)."
        ),
        business_meaning=(
            "Balance-Metrik für ganzheitliches Training. "
            "Zeigt ob mentale/meditative Praxis im Verhältnis zu physischem Training steht. "
            "Oft niedrig (<20) bei rein physisch orientierten Trainern."
        ),
        example_output="15",
        known_limitations=(
            "JSONB DEPENDENCY: training_types.abilities JSONB Feld muss populated sein. "
            "FORMULA: max-normalized (nicht prozentual vom Total). "
            "EDGE CASE: abilities NULL → 0% Mental. "
            "EDGE CASE: Alle Fähigkeiten = 0 → return 0."
        ),
        layer_2b_reuse_possible=True,
        **common_metadata_ability
    )

    ability_balance_mental_metadata.evidence.update(common_evidence_ability)
    ability_balance_mental_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
    ability_balance_mental_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED)
    ability_balance_mental_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
    ability_balance_mental_metadata.set_evidence("semantic_contract", EvidenceType.CODE_DERIVED)
    ability_balance_mental_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
    ability_balance_mental_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
    ability_balance_mental_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED)

    register_placeholder(ability_balance_mental_metadata)

    # ── ability_balance_coordination ──────────────────────────────────────────

    ability_balance_coordination_metadata = PlaceholderMetadata(
        key="ability_balance_coordination",
        description="Fähigkeiten-Balance-Score: Koordination (28d)",
        resolver_function="get_ability_balance_coordination",
        semantic_contract=(
            "Liefert einen normierten Score (0-100) für die relative Trainingsbelastung "
            "der Fähigkeit 'Koordination' über 28 Tage. Berechnung: "
            "(koordination_load / max_load_aller_fähigkeiten) × 100. "
            "Basiert auf training_types.abilities JSONB Feld (coordination_percentage)."
        ),
        business_meaning=(
            "Balance-Metrik für motorische Vielfalt. "
            "Zeigt ob Koordinations-Training (Kampfsport, Tanz, komplexe Bewegungsmuster) "
            "im Trainingsplan vertreten ist. Oft niedrig (<20) bei reinem Kraft/Ausdauer-Training."
        ),
        example_output="28",
        known_limitations=(
            "JSONB DEPENDENCY: training_types.abilities JSONB Feld muss populated sein. "
            "FORMULA: max-normalized (nicht prozentual vom Total). "
            "EDGE CASE: abilities NULL → 0% Koordination. "
            "EDGE CASE: Alle Fähigkeiten = 0 → return 0."
        ),
        layer_2b_reuse_possible=True,
        **common_metadata_ability
    )

    ability_balance_coordination_metadata.evidence.update(common_evidence_ability)
    ability_balance_coordination_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
    ability_balance_coordination_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED)
    ability_balance_coordination_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
    ability_balance_coordination_metadata.set_evidence("semantic_contract", EvidenceType.CODE_DERIVED)
    ability_balance_coordination_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
    ability_balance_coordination_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
    ability_balance_coordination_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED)

    register_placeholder(ability_balance_coordination_metadata)

    # ── ability_balance_mobility ──────────────────────────────────────────────

    ability_balance_mobility_metadata = PlaceholderMetadata(
        key="ability_balance_mobility",
        description="Fähigkeiten-Balance-Score: Mobilität (28d)",
        resolver_function="get_ability_balance_mobility",
        semantic_contract=(
            "Liefert einen normierten Score (0-100) für die relative Trainingsbelastung "
            "der Fähigkeit 'Mobilität' über 28 Tage. Berechnung: "
            "(mobilität_load / max_load_aller_fähigkeiten) × 100. "
            "Basiert auf training_types.abilities JSONB Feld (mobility_percentage)."
        ),
        business_meaning=(
            "Balance-Metrik für Beweglichkeits-Training. "
            "Zeigt ob Mobilität (Stretching, Yoga, Fasziendehnung) im Trainingsplan vertreten ist. "
            "Wichtig für Verletzungsprävention und Regeneration. Oft vernachlässigt (<20)."
        ),
        example_output="22",
        known_limitations=(
            "JSONB DEPENDENCY: training_types.abilities JSONB Feld muss populated sein. "
            "FORMULA: max-normalized (nicht prozentual vom Total). "
            "EDGE CASE: abilities NULL → 0% Mobilität. "
            "EDGE CASE: Alle Fähigkeiten = 0 → return 0."
        ),
        layer_2b_reuse_possible=True,
        **common_metadata_ability
    )

    ability_balance_mobility_metadata.evidence.update(common_evidence_ability)
    ability_balance_mobility_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
    ability_balance_mobility_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED)
    ability_balance_mobility_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
    ability_balance_mobility_metadata.set_evidence("semantic_contract", EvidenceType.CODE_DERIVED)
    ability_balance_mobility_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
    ability_balance_mobility_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
    ability_balance_mobility_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED)

    register_placeholder(ability_balance_mobility_metadata)

    # ── vo2max_trend_28d ──────────────────────────────────────────────────────

    vo2max_trend_28d_metadata = PlaceholderMetadata(
        key="vo2max_trend_28d",
        description="VO2 Max Trend über 28 Tage",
        category="Aktivität",
        resolver_module="backend/placeholder_resolver.py",
        resolver_function="_safe_float",
        data_layer_module="backend/data_layer/activity_metrics.py",
        data_layer_function="calculate_vo2max_trend_28d",
        source_tables=["vitals_baseline"],
        time_window="28d",
        output_type=OutputType.NUMERIC,
        placeholder_type=PlaceholderType.INTERPRETED,
        semantic_contract=(
            "Liefert den Trend der VO2 Max Werte über 28 Tage als Delta (letzter - erster Wert). "
            "Positiver Wert = Verbesserung der aeroben Fitness, "
            "negativer Wert = Verschlechterung. "
            "Basiert auf vitals_baseline.vo2_max (gemessen oder geschätzt)."
        ),
        business_meaning=(
            "Fitness-Trend-Metrik für aerobe Kapazität. "
            "Indikator für Trainingseffektivität bei Ausdauer-fokussiertem Training. "
            "QUESTIONABLE CATEGORY: VO2 Max ist primär Vitalwert/Recovery, nicht Activity."
        ),
        unit="ml/kg/min (Delta)",
        format_hint="Dezimalzahl mit Vorzeichen, typisch -3.0 bis +3.0",
        example_output="+1.2",
        minimum_data_requirements=None,
        quality_filter_policy=None,
        confidence_logic=(
            "datenpunktbasierte Coverage-Logik. "
            "Mindestens 2 VO2 Max Messungen in 28d erforderlich für Trend."
        ),
        missing_value_policy=MissingValuePolicy(
            available=False,
            value_raw=None,
            missing_reason="insufficient_data",
            legacy_display="nicht genug Daten"
        ),
        known_limitations=(
            "QUESTIONABLE CATEGORY: VO2 Max primär Recovery-Cluster, nicht Activity-Cluster. "
            "DEPENDENCY: vitals_baseline.vo2_max Feld (oft NULL wenn nicht gemessen). "
            "EDGE CASE: Nur 1 Messung → kein Trend → missing_value. "
            "EDGE CASE: Große Zeitlücken zwischen Messungen → Trend nicht aussagekräftig."
        ),
        layer_1_decision="Data Layer (activity_metrics.calculate_vo2max_trend_28d) — Kategorie diskutierbar",
        layer_2a_decision="Placeholder Resolver (_safe_float)",
        layer_2b_reuse_possible=True,
        architecture_alignment="Phase 0c Multi-Layer Architecture conform",
        issue_53_alignment="Layer separation established"
    )

    vo2max_trend_28d_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
    vo2max_trend_28d_metadata.set_evidence("category", EvidenceType.TO_VERIFY)
    vo2max_trend_28d_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED)
    vo2max_trend_28d_metadata.set_evidence("resolver_module", EvidenceType.CODE_DERIVED)
    vo2max_trend_28d_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
    vo2max_trend_28d_metadata.set_evidence("data_layer_module", EvidenceType.CODE_DERIVED)
    vo2max_trend_28d_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED)
    vo2max_trend_28d_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED)
    vo2max_trend_28d_metadata.set_evidence("time_window", EvidenceType.CODE_DERIVED)
    vo2max_trend_28d_metadata.set_evidence("output_type", EvidenceType.CODE_DERIVED)
    vo2max_trend_28d_metadata.set_evidence("placeholder_type", EvidenceType.MIXED)
    vo2max_trend_28d_metadata.set_evidence("semantic_contract", EvidenceType.MIXED)
    vo2max_trend_28d_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
    vo2max_trend_28d_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED)
    vo2max_trend_28d_metadata.set_evidence("format_hint", EvidenceType.DRAFT_DERIVED)
    vo2max_trend_28d_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
    vo2max_trend_28d_metadata.set_evidence("minimum_data_requirements", EvidenceType.UNRESOLVED)
    vo2max_trend_28d_metadata.set_evidence("quality_filter_policy", EvidenceType.UNRESOLVED)
    vo2max_trend_28d_metadata.set_evidence("confidence_logic", EvidenceType.CODE_DERIVED)
    vo2max_trend_28d_metadata.set_evidence("missing_value_policy", EvidenceType.CODE_DERIVED)
    vo2max_trend_28d_metadata.set_evidence("known_limitations", EvidenceType.MIXED)
    vo2max_trend_28d_metadata.set_evidence("layer_1_decision", EvidenceType.CODE_DERIVED)
    vo2max_trend_28d_metadata.set_evidence("layer_2a_decision", EvidenceType.CODE_DERIVED)
    vo2max_trend_28d_metadata.set_evidence("layer_2b_reuse_possible", EvidenceType.TO_VERIFY)
    vo2max_trend_28d_metadata.set_evidence("architecture_alignment", EvidenceType.CODE_DERIVED)
    vo2max_trend_28d_metadata.set_evidence("issue_53_alignment", EvidenceType.CODE_DERIVED)

    register_placeholder(vo2max_trend_28d_metadata)

    # ── activity_score ────────────────────────────────────────────────────────

    activity_score_metadata = PlaceholderMetadata(
        key="activity_score",
        description="Gesamtaktivitäts-Score (gewichtet)",
        category="Aktivität",
        resolver_module="backend/placeholder_resolver.py",
        resolver_function="_safe_int",
        data_layer_module="backend/data_layer/activity_metrics.py",
        data_layer_function="calculate_activity_score",
        source_tables=["activity_log", "training_types", "rest_days", "vitals_baseline", "user_focus_area_weights"],
        time_window="composite (7d, 14d, 28d mixed)",
        output_type=OutputType.NUMERIC,
        placeholder_type=PlaceholderType.SCORE,
        semantic_contract=(
            "Liefert einen gewichteten Gesamtscore (0-100) für Trainingsaktivität. "
            "Berechnung: Gewichteter Durchschnitt aus 5 Komponenten: "
            "1. Volumen (training_minutes_week vs. Ziel), "
            "2. Frequenz (training_frequency_7d vs. Ziel), "
            "3. Qualität (quality_sessions_pct), "
            "4. Balance (Ability Balance Scores), "
            "5. Recovery (rest_day_compliance). "
            "Gewichtung dynamisch aus user_focus_area_weights."
        ),
        business_meaning=(
            "Meta-Score für Gesamtaktivitäts-Qualität. "
            "Kombiniert Volumen, Konsistenz, Qualität, Vielfalt und Regeneration. "
            "Ziel-abhängig: Gewichtung variiert je nach User-Fokus (Kraft vs. Ausdauer vs. Balance)."
        ),
        unit="Score (0-100)",
        format_hint="Ganzzahl",
        example_output="78",
        minimum_data_requirements=None,
        quality_filter_policy=None,
        confidence_logic=(
            "Composite Confidence basierend auf Komponenten-Confidence. "
            "Mindestens 3/5 Komponenten müssen verfügbar sein für Score-Berechnung."
        ),
        missing_value_policy=MissingValuePolicy(
            available=False,
            value_raw=None,
            missing_reason="insufficient_data",
            legacy_display="nicht genug Daten"
        ),
        known_limitations=(
            "COMPOSITE SCORE: Komplexe Abhängigkeiten zu allen Activity-Metriken. "
            "FOCUS-DEPENDENT: Gewichtung variiert je nach user_focus_area_weights (dynamisch). "
            "EDGE CASE: Wenn user_focus_area_weights fehlt → default weighting (alle gleich). "
            "ZEITFENSTER MIXED: Komponenten nutzen unterschiedliche Zeitfenster (7d, 14d, 28d). "
            "QUESTIONABLE: Vermischt Metriken mit unterschiedlicher Verlässlichkeit "
            "(z.B. quality_sessions_pct hat TO_VERIFY Issues)."
        ),
        layer_1_decision="Data Layer (activity_metrics.calculate_activity_score)",
        layer_2a_decision="Placeholder Resolver (_safe_int)",
        layer_2b_reuse_possible=False,
        architecture_alignment="Phase 0c Multi-Layer Architecture conform",
        issue_53_alignment="Layer separation established"
    )

    activity_score_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
    activity_score_metadata.set_evidence("category", EvidenceType.CODE_DERIVED)
    activity_score_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED)
    activity_score_metadata.set_evidence("resolver_module", EvidenceType.CODE_DERIVED)
    activity_score_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
    activity_score_metadata.set_evidence("data_layer_module", EvidenceType.CODE_DERIVED)
    activity_score_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED)
    activity_score_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED)
    activity_score_metadata.set_evidence("time_window", EvidenceType.CODE_DERIVED)
    activity_score_metadata.set_evidence("output_type", EvidenceType.CODE_DERIVED)
    activity_score_metadata.set_evidence("placeholder_type", EvidenceType.MIXED)
    activity_score_metadata.set_evidence("semantic_contract", EvidenceType.CODE_DERIVED)
    activity_score_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
    activity_score_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED)
    activity_score_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED)
    activity_score_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
    activity_score_metadata.set_evidence("minimum_data_requirements", EvidenceType.UNRESOLVED)
    activity_score_metadata.set_evidence("quality_filter_policy", EvidenceType.UNRESOLVED)
    activity_score_metadata.set_evidence("confidence_logic", EvidenceType.MIXED)
    activity_score_metadata.set_evidence("missing_value_policy", EvidenceType.CODE_DERIVED)
    activity_score_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED)
    activity_score_metadata.set_evidence("layer_1_decision", EvidenceType.CODE_DERIVED)
    activity_score_metadata.set_evidence("layer_2a_decision", EvidenceType.CODE_DERIVED)
    activity_score_metadata.set_evidence("layer_2b_reuse_possible", EvidenceType.CODE_DERIVED)
    activity_score_metadata.set_evidence("architecture_alignment", EvidenceType.CODE_DERIVED)
    activity_score_metadata.set_evidence("issue_53_alignment", EvidenceType.CODE_DERIVED)

    register_placeholder(activity_score_metadata)


# =============================================================================
# Auto-Registration
# =============================================================================

# Register all groups when module is imported
register_activity_group_1()
register_activity_group_2()
register_activity_group_3()
