#pragma once

#include <QVector2D>

#include "entt/entity/entity.hpp"

#include "BehaviorKind.h"
#include "DynamicBodyComponent.h"
#include "EntityAdmin.h"
#include "ModuleOwnerComponent.h"
#include "MovementIntentComponent.h"
#include "PositionComponent.h"
#include "SelectedBehaviorComponent.h"

// Shared executor body for the behaviors that orbit a single target entity and then
// hand that target to the ship's in-range modules (REQ-SHP-ORBIT): Attack (with
// WeaponComponent) and Repair (with RepairToolComponent).
//
// Two passes, in this order — the order and the exact sequence of component writes
// are load-bearing for determinism (see the Tick Order section of
// docs/architecture.md):
//   1. Ships that have `Behavior` and won with `kind` write their MovementIntent to
//      orbit the behavior's target at the behavior's orbit radius. A target that is
//      gone (or has no position) degenerates to "hold position": the ship's own
//      position with a zero radius.
//   2. Modules of type `ModuleComponent` whose owner won with `kind` adopt the
//      behavior's target, but only when it lies within that module's own range.
//      Out-of-range modules keep whatever target they already had, which
//      CombatSystem/RepairSystem re-validate.
//
// `Behavior` must expose `std::optional<entt::entity> currentTarget` and
// `float orbitRadius_tiles`; `ModuleComponent` must expose `float range_tiles` and
// `std::optional<entt::entity> currentTarget`.
template <typename Behavior, typename ModuleComponent>
void executeOrbitAndAssign(EntityAdmin& admin, BehaviorKind kind)
{
    // Ships: move toward the behavior target.
    admin.forEach<Behavior, SelectedBehaviorComponent, PositionComponent,
                  MovementIntentComponent>(
        [&](entt::entity /*e*/, const Behavior& behavior,
            const SelectedBehaviorComponent& selected, const PositionComponent& pos,
            MovementIntentComponent& intent)
        {
            if (selected.winner != kind) { return; }
            if (!behavior.currentTarget) { return; }

            const entt::entity t = *behavior.currentTarget;
            QVector2D center = pos.value;
            float     radius = 0.0f;
            QVector2D centerVelocity;
            if (admin.isValid(t) && admin.hasAll<PositionComponent>(t))
            {
                center = admin.get<PositionComponent>(t).value;
                radius = behavior.orbitRadius_tiles;
                if (admin.hasAll<DynamicBodyComponent>(t))
                {
                    centerVelocity = admin.get<DynamicBodyComponent>(t).velocity_tpt;
                }
            }
            intent = MovementIntentComponent{true, center, radius, centerVelocity};
        });

    // Modules: assign the behavior target only if it is within this module's range.
    admin.forEach<ModuleComponent, ModuleOwnerComponent>(
        [&](entt::entity /*me*/, ModuleComponent& module,
            const ModuleOwnerComponent& owner)
        {
            if (!admin.hasAll<Behavior, SelectedBehaviorComponent>(owner.owner))
            {
                return;
            }
            const SelectedBehaviorComponent& selected =
                admin.get<SelectedBehaviorComponent>(owner.owner);
            if (selected.winner != kind) { return; }

            const Behavior& behavior = admin.get<Behavior>(owner.owner);
            if (!behavior.currentTarget) { return; }

            const entt::entity t = *behavior.currentTarget;
            if (!admin.isValid(t) || !admin.hasAll<PositionComponent>(t)) { return; }

            const QVector2D ownerPos = admin.get<PositionComponent>(owner.owner).value;
            const float dist = (admin.get<PositionComponent>(t).value - ownerPos).length();
            if (dist <= module.range_tiles)
            {
                module.currentTarget = t;
            }
        });
}
