extends Node

const BUILDING_SCENE: PackedScene = preload("res://scenes/entities/building/building.tscn")
const GRID_SIZE: float = 1.0

@onready var inventory_system: Node = get_node("../InventorySystem")

var blueprints: Array = []

func _ready() -> void:
  blueprints = [
    {"id": &"floor", "name": "Floor", "size": Vector3(1, 0.2, 1), "color": Color(0.55, 0.4, 0.25), "material": &"wood", "cost": 1},
    {"id": &"wall", "name": "Wall", "size": Vector3(1, 2, 0.2), "color": Color(0.7, 0.6, 0.45), "material": &"wood", "cost": 1},
    {"id": &"door", "name": "Door", "size": Vector3(1, 2, 0.15), "color": Color(0.4, 0.25, 0.15), "material": &"wood", "cost": 2},
    {"id": &"roof", "name": "Roof", "size": Vector3(1, 0.2, 1), "color": Color(0.5, 0.3, 0.2), "material": &"wood", "cost": 1}
  ]

func _building_root() -> Node3D:
  var n: Node = get_node_or_null("/root/World/EntityRoot/Buildings")
  if n == null:
    n = get_node_or_null("/root/Dungeon/EntityRoot/Buildings")
  return n

func get_blueprints() -> Array:
  return blueprints

func find_blueprint(id: StringName) -> Dictionary:
  for b in blueprints:
    if b.id == id:
      return b
  return {}

func snap_position(pos: Vector3) -> Vector3:
  return Vector3(round(pos.x / GRID_SIZE) * GRID_SIZE, max(0.0, pos.y), round(pos.z / GRID_SIZE) * GRID_SIZE)

func place(player: Node, id: StringName, pos: Vector3, rot: float) -> bool:
  if not (multiplayer.is_server() or multiplayer.multiplayer_peer == null):
    request_place.rpc_id(1, player.get_path(), id, pos, rot)
    return false
  var bp: Dictionary = find_blueprint(id)
  if bp.is_empty():
    return false
  var mat: StringName = bp.material
  var cost: int = bp.cost
  if inventory_system.get_amount(player, mat) < cost:
    return false
  inventory_system.remove_item(player, mat, cost)
  var root := _building_root()
  if root == null:
    return false
  var b: StaticBody3D = BUILDING_SCENE.instantiate()
  b.building_id = id
  b.name = "B_%s_%d" % [str(id), Time.get_ticks_msec() + randi() % 1000]
  root.add_child(b, true)
  b.global_position = snap_position(pos)
  b.rotation.y = rot
  b.apply_building(id)
  _apply_building_visual.rpc(b.get_path(), id)
  EventBus.building_placed.emit(b)
  return true

func remove(player: Node, b_path: NodePath) -> bool:
  if not (multiplayer.is_server() or multiplayer.multiplayer_peer == null):
    request_remove.rpc_id(1, player.get_path(), b_path)
    return false
  var node: Node = get_node_or_null(b_path)
  if node == null:
    return false
  var bp: Dictionary = find_blueprint(node.building_id)
  if not bp.is_empty():
    inventory_system.add_item(player, bp.material, max(1, int(float(bp.cost) * 0.5)))
  EventBus.building_removed.emit(node)
  node.queue_free()
  return true

@rpc("any_peer", "reliable")
func request_place(player_path: NodePath, id: StringName, pos: Vector3, rot: float) -> void:
  var p: Node = get_node_or_null(player_path)
  if p:
    place(p, id, pos, rot)

@rpc("any_peer", "reliable")
func request_remove(player_path: NodePath, b_path: NodePath) -> void:
  var p: Node = get_node_or_null(player_path)
  if p:
    remove(p, b_path)

@rpc("authority", "reliable", "call_local")
func _apply_building_visual(b_path: NodePath, id: StringName) -> void:
  var node: Node = get_node_or_null(b_path)
  if node and node.has_method("apply_building"):
    node.apply_building(id)
