class_name Door extends StaticBody3D

signal opened
signal closed
signal locked
signal unlocked

@export var is_open: bool = false
@export var is_locked: bool = false
@export var key: Item

var requires_key: bool:
	get: return key != null

class UnopenableDoorError:
	static var name:
		get: return "UnopenableDoorError"
class NodeNoInventoryError:
	static var name:
		get: return "NodeNoInventoryError"
class MissingKeyError:
	static var name:
		get: return "MissingKeyError"
class LockedError:
	static var name:
		get: return "LockedError"

func check_requirements(interactor: Node) -> Result:
	if not is_locked: return Result.Unit
	elif not requires_key: return Result.err(UnopenableDoorError)

	match NodeExt.find_child_variant(interactor, ItemContainer):
		var opt when opt.is_none(): return Result.err(NodeNoInventoryError)
		var opt: match opt.unwrap():
			var inv when inv.has_item(key): return Result.Unit
			_: return Result.err(MissingKeyError)

func open() -> Result:
	if is_locked: return Result.err(LockedError)
	if not is_open:
		is_open = true
		opened.emit()
	return Result.Unit

func close() -> Result:
	if is_open:
		is_open = false
		closed.emit()
	return Result.Unit

func unlock() -> Result:
	if is_locked:
		is_locked = false
		unlocked.emit()
	return Result.Unit

func lock() -> Result:
	if not is_locked:
		is_locked = true
		locked.emit()
	return Result.Unit

func try_unlock(node: Node) -> Result:
	match check_requirements(node):
		var result when result.is_ok(): return unlock()
		var err: return err

func try_open(node: Node) -> Result:
	if is_locked: return try_unlock(node)
	else: return open()

func try_interact(node: Node) -> Result:
	if is_open: return close()
	else: return try_open(node)

func _on_interact(interactor: Interactor):
	match try_interact(interactor.root_node):
		var ok when ok.is_ok(): pass
		var err: push_error(err.unwrap_err().name)
