class_name Magnet
extends StaticBody2D
## Магнит с полюсами N и S.
## Создаёт силовое поле, действующее на MagneticObject.

## Сила магнита (чем больше — тем сильнее притяжение/отталкивание)
@export var strength: float = 50000.0

## Радиус действия магнитного поля
@export var field_radius: float = 300.0

## Нельзя перемещать
@export var is_fixed: bool = false

## Размер клетки сетки
@export var grid_size: int = 64

## Полярность: true = северный полюс сверху, false = южный сверху
@export var north_up: bool = true

## Drag-and-drop
var dragging: bool = false
var drag_offset: Vector2 = Vector2.ZERO

## Объекты в зоне действия
var affected_bodies: Array[RigidBody2D] = []

@onready var field_area := $FieldArea


func _ready() -> void:
	add_to_group("magnets")

	if field_area:
		# Настроить радиус зоны действия
		var shape := field_area.get_node("CollisionShape2D")
		if shape and shape.shape is CircleShape2D:
			shape.shape.radius = field_radius

		field_area.body_entered.connect(_on_body_entered)
		field_area.body_exited.connect(_on_body_exited)

	_update_visual()


func _physics_process(_delta: float) -> void:
	for body in affected_bodies:
		if not is_instance_valid(body):
			continue
		_apply_magnetic_force(body)


## Применить магнитную силу к объекту
func _apply_magnetic_force(body: RigidBody2D) -> void:
	var direction := global_position - body.global_position
	var distance := direction.length()

	if distance < 10.0:
		return  # Защита от бесконечной силы

	# Сила обратно пропорциональна квадрату расстояния: F = k / r²
	var force_magnitude := strength / (distance * distance)

	# Определяем притяжение или отталкивание
	# Если объект — магнит со своей полярностью, учитываем взаимодействие полюсов
	var attract := true
	if body.has_method("get_polarity"):
		# Одноимённые полюса отталкиваются
		var body_polarity: bool = body.get_polarity()
		attract = (north_up != body_polarity)  # Разные полюса — притяжение

	var force := direction.normalized() * force_magnitude
	if not attract:
		force = -force

	body.apply_central_force(force)


func _on_body_entered(body: Node2D) -> void:
	if body is RigidBody2D and body.is_in_group("magnetic"):
		affected_bodies.append(body)


func _on_body_exited(body: Node2D) -> void:
	if body is RigidBody2D:
		affected_bodies.erase(body)


## Перевернуть полярность
func flip_polarity() -> void:
	north_up = not north_up
	_update_visual()


func _update_visual() -> void:
	# Северный полюс — красный, южный — синий
	if north_up:
		modulate = Color(1.0, 0.3, 0.3)  # Красноватый (N сверху)
	else:
		modulate = Color(0.3, 0.3, 1.0)  # Синеватый (S сверху)


# --- Drag-and-drop ---

func _input(event: InputEvent) -> void:
	if is_fixed:
		return

	if event is InputEventMouseButton:
		if event.button_index == MOUSE_BUTTON_LEFT:
			if event.pressed and _is_mouse_over():
				dragging = true
				drag_offset = global_position - event.position
				z_index = 10
			elif not event.pressed and dragging:
				dragging = false
				_snap_to_grid()
				z_index = 0
		elif event.button_index == MOUSE_BUTTON_RIGHT:
			if event.pressed and _is_mouse_over():
				flip_polarity()

	if event is InputEventMouseMotion and dragging:
		global_position = event.position + drag_offset


func _snap_to_grid() -> void:
	position = Vector2(
		round(position.x / grid_size) * grid_size,
		round(position.y / grid_size) * grid_size
	)


func _is_mouse_over() -> bool:
	var mouse_pos := get_global_mouse_position()
	var rect := Rect2(
		global_position - Vector2(grid_size, grid_size) / 2,
		Vector2(grid_size, grid_size)
	)
	return rect.has_point(mouse_pos)
