///|
pub(all) enum SceneDiagnostic {
  EmptyTitle
  EmptyElementId(index~ : Int)
  DuplicateElementId(id~ : String)
  InvalidRect(id~ : String)
  EmptyAssetId(id~ : String)
  EmptyImageLabel(id~ : String)
  TextOutsideCanvas(id~ : String)
} derive(Debug, Eq)

///|
pub fn element_ids(document : PosterDocument) -> Array[String] {
  document.elements.map(element_id)
}

///|
fn element_id(element : Element) -> String {
  match element {
    Block(id~, rect=_, fill=_, radius=_) => id
    Text(id~, content=_, position=_, style=_, max_width=_) => id
    ImageSlot(id~, asset_id=_, rect=_, label=_, fit=_) => id
  }
}

///|
fn has_id(ids : Array[String], target : String) -> Bool {
  ids.any(id => id == target)
}

///|
fn rect_is_invalid(rect : Rect) -> Bool {
  rect.width <= 0 || rect.height <= 0
}

///|
pub fn validate_document(
  document : PosterDocument,
  strict~ : Bool,
) -> Array[SceneDiagnostic] {
  let diagnostics : Array[SceneDiagnostic] = []
  if strict && document.title.trim().is_empty() {
    diagnostics.push(SceneDiagnostic::EmptyTitle)
  }
  let ids : Array[String] = []
  document.elements.eachi((index, element) => {
    let id = element_id(element)
    if id.trim().is_empty() {
      diagnostics.push(SceneDiagnostic::EmptyElementId(index~))
    } else if has_id(ids, id) {
      diagnostics.push(SceneDiagnostic::DuplicateElementId(id~))
    } else {
      ids.push(id)
    }
    match element {
      Block(id~, rect~, fill=_, radius~) => {
        if rect_is_invalid(rect) {
          diagnostics.push(InvalidRect(id~))
        }
        if radius < 0 {
          diagnostics.push(InvalidRect(id~))
        }
      }
      Text(id~, content=_, position~, style=_, max_width~) => {
        if max_width <= 0 {
          diagnostics.push(InvalidRect(id~))
        }
        if position.x < 0 ||
          position.y < 0 ||
          position.x >= document.preset.canvas.width ||
          position.y >= document.preset.canvas.height {
          diagnostics.push(TextOutsideCanvas(id~))
        }
      }
      ImageSlot(id~, asset_id~, rect~, label~, fit=_) => {
        if rect_is_invalid(rect) {
          diagnostics.push(InvalidRect(id~))
        }
        if asset_id.trim().is_empty() {
          diagnostics.push(EmptyAssetId(id~))
        }
        if label.trim().is_empty() {
          diagnostics.push(EmptyImageLabel(id~))
        }
      }
    }
  })
  diagnostics
}

///|
pub fn diagnostic_code(diagnostic : SceneDiagnostic) -> String {
  match diagnostic {
    EmptyTitle => "empty-title"
    EmptyElementId(index=_) => "empty-element-id"
    DuplicateElementId(id=_) => "duplicate-element-id"
    InvalidRect(id=_) => "invalid-rect"
    EmptyAssetId(id=_) => "empty-asset-id"
    EmptyImageLabel(id=_) => "empty-image-label"
    TextOutsideCanvas(id=_) => "text-outside-canvas"
  }
}