"""Format visual rules interface.

Every format that supports visual pipeline compilation provides a
visual_rules.py in its format directory. This module defines the
dataclasses that visual_rules.py must instantiate.

Usage:
    from visual.format_interface import FormatVisualRules, BeatPolicy
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Dict, List, Optional


@dataclass
class BeatPolicy:
    """Composition and element policy for a specific beat type.

    Controls how many character LoRAs are injected and what
    composition style the prompt engine should enforce.
    """
    max_loras: int = 4
    composition_style: str = "ENSEMBLE"  # FACE_DOMINANT, ENSEMBLE, ENVIRONMENT
    allow_kinetic_prompting: bool = True


@dataclass
class GrammarBleedSchedule:
    """Defines how per-character lens grammars contaminate across exposures.

    Each exposure level maps to a bleed percentage (0.0 = pure grammar,
    1.0 = fully merged with other character's grammar).
    """
    exposure_bleed: Dict[int, float] = field(default_factory=lambda: {
        1: 0.0,   # Pure character grammar
        2: 0.15,  # First bleed artifacts
        3: 0.5,   # Dominant contamination
        4: 1.0,   # Full merge
    })


@dataclass
class FormatVisualRules:
    """Visual compilation rules for a format.

    Read by the visual compiler to configure prompt engine behavior,
    element selection, and critic validation per format.
    """
    # ── Prompt Engine Config ──────────────────────────────────────
    mute_layers: List[str] = field(default_factory=list)
    force_presets: Dict[str, str] = field(default_factory=dict)
    grammar_injection_layer: Optional[int] = None
    supports_grammar: bool = False

    # ── Element & Composition (Per-Beat) ──────────────────────────
    default_beat_policy: BeatPolicy = field(
        default_factory=lambda: BeatPolicy(4, "ENSEMBLE", True)
    )
    beat_policies: Dict[str, BeatPolicy] = field(default_factory=dict)

    # ── Grammar Bleed ─────────────────────────────────────────────
    bleed_schedule: GrammarBleedSchedule = field(
        default_factory=GrammarBleedSchedule
    )

    # ── Execution Defaults (for Render Manifest) ──────────────────
    default_engine: str = "kling"
    default_profile: str = "kling-v1.5-pro-standard"
    aspect_ratio: str = "9:16"
    default_duration: int = 5

    # ── Critic Injections ─────────────────────────────────────────
    format_check_questions: List[str] = field(default_factory=list)

    def get_beat_policy(self, beat_name: str) -> BeatPolicy:
        """Get composition policy for a beat, falling back to default."""
        return self.beat_policies.get(beat_name, self.default_beat_policy)

    def is_layer_active(self, layer_name: str) -> bool:
        """Check if a prompt layer should be active for this format."""
        return layer_name not in self.mute_layers

    def get_bleed_factor(self, exposure_level: int) -> float:
        """Get grammar bleed percentage for an exposure level."""
        return self.bleed_schedule.exposure_bleed.get(exposure_level, 0.0)
