//! VS1 — View coverage types shared between the engine and view-sdk.
//!
//! These types live in the base SDK so neither the engine nor `view-sdk`
//! needs to depend on the other. The engine uses them to compute
//! `ViewCoverageReport`; view-sdk re-exports them for view plugin authors.

/// Board rendering contexts a view plugin is designed for.
///
/// The default set returned by `ViewPlugin::supported_canvas_modes` is
/// `CanvasMode::all_2d()`, which covers every board topology the engine
/// currently supports. `Scene3D` is reserved for future 3D rendering.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CanvasMode {
    /// Rectangular tile grid — the most common case (chess, checkers, etc.).
    Grid,
    /// Hexagonal grid (Catan, hex-strategy games).
    Hex,
    /// Arbitrary 2D freeform space (TTRPG dungeon maps, etc.).
    Freeform,
    /// Node-edge graph board (network games, route-builders).
    Graph,
    /// Explicit 3D scene (future; not yet produced by any board type).
    Scene3D,
}

impl CanvasMode {
    /// All 2D canvas modes — the safe default for view plugins that do not
    /// require a 3D renderer.
    pub fn all_2d() -> Vec<Self> {
        vec![Self::Grid, Self::Hex, Self::Freeform, Self::Graph]
    }
}

/// Whether a view's declared canvas modes are compatible with the session's
/// board topology.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CanvasModeStatus {
    /// View explicitly lists the required mode (or all modes are 2D and the
    /// board is also 2D).
    Supported,
    /// View is 2D-only but the session uses a 3D scene. The engine will render
    /// the view's 2D assets as billboards at their world positions.
    Billboard,
    /// No viable rendering path exists (e.g., view requires 3D meshes but the
    /// board has no 3D renderer). Session creation should warn the host.
    Incompatible,
}

/// A single entity-local-name → asset-key mapping declared by a view plugin.
///
/// View plugins return a `Vec<ViewAssetDeclaration>` from
/// `ViewPlugin::declared_entity_coverage`. An empty vec (the default) means
/// the view provides no entity-specific overrides — all entities fall through
/// to the universal labeled-token renderer.
#[derive(Debug, Clone)]
pub struct ViewAssetDeclaration {
    /// snake_case struct name of the entity type, e.g. `"bishop"`, `"piece"`.
    /// Matched case-insensitively against `EntityTypeDeclaration::local_name`.
    pub local_name:  &'static str,
    /// Asset key for the board (Tangible) rendering context.
    /// `None` if the view has no specific board asset for this entity.
    pub board_asset: Option<&'static str>,
    /// Asset key for the sidebar/UI (Featured) rendering context.
    /// `None` if the view has no specific UI asset for this entity.
    pub ui_asset:    Option<&'static str>,
}

/// Whether a view provides an explicit asset or falls back to the universal
/// renderer for a specific entity in a specific display context.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CoverageStatus {
    /// The view declared a specific asset (board or UI) for this entity.
    Explicit,
    /// No view-specific asset; the universal labeled-token renderer is used.
    Fallback,
}

/// Coverage status for a single entity type across both display contexts.
#[derive(Debug, Clone)]
pub struct EntityCoverageEntry {
    /// Local name of the entity (snake_case struct name).
    pub local_name:   String,
    /// Coverage for the board (Tangible) context.
    /// `None` if the entity is not Tangible — no board asset is needed.
    pub board_status: Option<CoverageStatus>,
    /// Coverage for the sidebar/UI (Featured) context.
    /// `None` if the entity is not Featured — no UI asset is needed.
    pub ui_status:    Option<CoverageStatus>,
}

impl EntityCoverageEntry {
    /// `true` if every display context this entity participates in has an
    /// explicit asset from the view plugin.
    pub fn is_fully_covered(&self) -> bool {
        let board_ok = self.board_status.is_none_or(|s| s == CoverageStatus::Explicit);
        let ui_ok    = self.ui_status.is_none_or(|s| s == CoverageStatus::Explicit);
        board_ok && ui_ok
    }
}

/// Pre-session diagnostic produced by
/// `maat_engine::view_coverage::compute_view_coverage`.
#[derive(Debug, Clone)]
pub struct ViewCoverageReport {
    /// One entry per entity type declared by the model plugin.
    pub entries:            Vec<EntityCoverageEntry>,
    /// Whether the view's declared canvas modes cover the session's board.
    pub canvas_mode_status: CanvasModeStatus,
}

impl ViewCoverageReport {
    /// `true` if every entity has explicit assets in every display context
    /// it participates in, AND the canvas mode is supported.
    pub fn is_fully_covered(&self) -> bool {
        self.canvas_mode_status == CanvasModeStatus::Supported
            && self.entries.iter().all(|e| e.is_fully_covered())
    }

    /// Number of entity types that have at least one fallback display context.
    pub fn fallback_count(&self) -> usize {
        self.entries.iter().filter(|e| !e.is_fully_covered()).count()
    }
}
