// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
/// Internal component that tracks a child entity's relationship to its parent.
///
/// Fields:
///
/// * `parent` : The parent entity that this child is attached to.
///
priv struct Child {
  parent : Entity
}

///|
/// Internal component that tracks a parent entity's children.
///
/// Fields:
///
/// * `children` : Array of all child entities attached to this parent.
/// * `is_root` : Whether this parent is a root entity (a parent that is not
///   itself a child of another entity). Root entities are the starting points
///   for hierarchical transform propagation in the unified
///   `@transform.transform_propagate_system`.
///
priv struct Parent {
  children : Array[Entity]
  is_root : Bool
}

///|
priv struct HierarchyWorldStore {
  parents : Map[Entity, Parent]
  children : Map[Entity, Child]
}

///|
let hierarchy_world_stores : Map[UInt, HierarchyWorldStore] = Map([])

///|
fn hierarchy_store() -> HierarchyWorldStore {
  let world = @ecs.require_current_world()
  hierarchy_world_stores.get_or_init(world.id(), () => {
    parents: Map([]),
    children: Map([]),
  })
}

///|
fn hierarchy_parents() -> Map[Entity, Parent] {
  hierarchy_store().parents
}

///|
fn hierarchy_children() -> Map[Entity, Child] {
  hierarchy_store().children
}

///|
fn hierarchy_would_create_cycle(child : Entity, parent : Entity) -> Bool {
  let children = hierarchy_children()
  let mut current : Entity? = Some(parent)
  while current is Some(entity) {
    if entity == child {
      return true
    }
    current = children.get(entity).map(rel => rel.parent)
  }
  false
}

///|
fn detach_parent_link(child : Entity) -> Unit {
  let parents = hierarchy_parents()
  let children = hierarchy_children()
  match children.get(child) {
    Some(rel) => {
      children.remove(child)
      match parents.get(rel.parent) {
        Some(parent_record) => {
          let updated_children = parent_record.children.filter(fn(entity) {
            entity != child
          })
          if updated_children.length() == 0 {
            parents.remove(rel.parent)
          } else {
            parents.set(rel.parent, {
              ..parent_record,
              children: updated_children,
            })
          }
        }
        None => ()
      }
    }
    None => ()
  }
  match parents.get(child) {
    Some(parent_record) =>
      parents.set(child, { ..parent_record, is_root: true })
    None => ()
  }
}

///|
fn attach_parent_link(child : Entity, parent : Entity) -> Unit {
  let parents = hierarchy_parents()
  let children = hierarchy_children()
  let parent_record = parents.get_or_init(parent, () => {
    children: [],
    is_root: !children.contains(parent),
  })
  if !parent_record.children.contains(child) {
    let updated_children : Array[Entity] = []
    for entity in parent_record.children {
      updated_children.push(entity)
    }
    updated_children.push(child)
    parents.set(parent, { ..parent_record, children: updated_children })
  }
  children.set(child, { parent, })
  match parents.get(child) {
    Some(child_parent_record) =>
      parents.set(child, { ..child_parent_record, is_root: false })
    None => ()
  }
}

///|
/// Creates a new child entity attached to the specified parent entity.
///
/// The child entity is automatically registered in the entity system and linked
/// to its parent. The parent-child relationship enables hierarchical transforms
/// and cascading lifecycle operations (destroying a parent destroys all its
/// children, and respawning a parent respawns all its children).
///
/// Parameters:
///
/// * `parent` : The parent entity to attach the new child to.
/// Returns a new child `Entity` that is linked to the parent.
///
/// Example:
///
/// ```moonbit nocheck
/// let parent = @entity.Entity()
/// @transform.transforms().set(parent, @transform.Transform::from_xyz(100.0, 100.0, 0.0))
///
/// let child = parent.spawn_child()
/// @transform.transforms().set(child, @transform.Transform::from_xyz(10.0, 5.0, 0.0))
///
/// inspect(parent.get_children().length(), content="1")
/// inspect(child.is_child(), content="true")
///
/// parent.destroy()
/// ```
///
pub fn Entity::spawn_child(parent : Entity) -> Entity {
  let child_entity = Entity()
  attach_parent_link(child_entity, parent)
  child_entity
}

///|
/// Retrieves all child entities attached to the specified parent entity.
///
/// Parameters:
///
/// * `parent` : The parent entity whose children to retrieve.
///
/// Returns an array of child entities. Returns an empty array if the entity has
/// no children.
///
/// Example:
///
/// ```moonbit nocheck
/// let parent = @entity.Entity()
/// let child1 = parent.spawn_child()
/// let child2 = parent.spawn_child()
///
/// let children = parent.get_children()
/// inspect(children.length(), content="2")
/// inspect(child1 == children[0], content="true")
/// inspect(child2 == children[1], content="true")
///
/// parent.destroy()
/// ```
///
pub fn Entity::get_children(parent : Entity) -> Array[Entity] {
  let parents = hierarchy_parents()
  parents.get(parent).map_or([], p => p.children)
}

///|
/// Checks whether an entity is a child of another entity.
///
/// Parameters:
///
/// * `entity` : The entity to check.
///
/// Returns `true` if the entity is a child (has a parent), `false` otherwise.
///
/// Example:
///
/// ```moonbit nocheck
/// let parent = @entity.Entity()
/// let child = parent.spawn_child()
/// let standalone = @entity.Entity()
///
/// inspect(child.is_child(), content="true")
/// inspect(standalone.is_child(), content="false")
///
/// parent.destroy()
/// standalone.destroy()
/// ```
///
pub fn Entity::is_child(entity : Entity) -> Bool {
  let children = hierarchy_children()
  children.contains(entity)
}

///|
/// Retrieves the parent entity of a child entity.
///
/// Parameters:
///
/// * `child` : The child entity whose parent to retrieve.
///
/// Returns `Some(parent_entity)` if the entity has a parent, or `None` if the
/// entity is not a child.
///
/// Example:
///
/// ```moonbit nocheck
/// let parent = @entity.Entity()
/// let child = parent.spawn_child()
///
/// inspect(child.get_parent().unwrap() == parent, content="true")
/// inspect(parent.get_parent(), content="None")
///
/// parent.destroy()
/// ```
///
pub fn Entity::get_parent(child : Entity) -> Entity? {
  let children = hierarchy_children()
  children.get(child).map(c => c.parent)
}

///|
pub fn Entity::set_parent(child : Entity, parent : Entity) -> Unit {
  guard child != parent else { return }
  guard !hierarchy_would_create_cycle(child, parent) else { return }
  guard child.get_parent() != Some(parent) else { return }
  detach_parent_link(child)
  attach_parent_link(child, parent)
}

///|
pub fn Entity::remove_parent(child : Entity) -> Unit {
  detach_parent_link(child)
}

///|
/// Returns an iterator over all root entities in the hierarchy.
///
/// Root entities are parent entities (entities that have spawned children) that
/// are not themselves children of another entity. They serve as the starting
/// points for hierarchical transform propagation in the
/// `@transform.transform_propagate_system`.
///
/// **Important**: Only entities that have called `spawn_child()` at least once
/// can be root entities. Standalone entities created with `Entity()` that
/// have never spawned children are not considered roots and will not be returned
/// by this function.
///
/// Returns an `Iter[Entity]` that yields all root parent entities.
///
/// Example:
///
/// ```moonbit nocheck
/// @entity.entity_flush_system(0.0)
///
/// let root1 = @entity.Entity()
/// let root2 = @entity.Entity()
/// let child1 = root1.spawn_child()
/// let _grandchild = child1.spawn_child()
///
/// // root1 and root2 are roots (parents but not children)
/// // child1 is not a root (it's both a parent and a child)
/// let roots = @entity.get_roots().collect()
/// inspect(roots.length(), content="1")
///
/// root1.destroy()
/// root2.destroy()
/// ```
///
pub fn get_roots() -> Iter[Entity] {
  let parents = hierarchy_parents()
  parents.iter().filter(ep => ep.1.is_root && ep.0.is_alive()).map(ep => ep.0)
}

///|