#pragma once

#include "common/math/GameMath.h"
#include <frostbite2D/types/type_math.h>
#include <string>
#include <unordered_map>
#include <utility>
#include <variant>
#include <vector>

namespace frostbite2D {

/// 输入层产出的统一命令，屏蔽键盘与手柄的设备差异。
enum class CharacterCommandType {
  Move,
  JumpPressed,
  ActionPressed,
};

enum class CharacterMoveMode {
  None,
  Walk,
  Run,
};

/// 单条输入命令。移动命令携带轴值，按钮命令只关心触发时机。
struct CharacterCommand {
  CharacterCommandType type = CharacterCommandType::Move;
  Vec2 moveAxis = Vec2::Zero();
  float timestamp = 0.0f;
  CharacterMoveMode moveMode = CharacterMoveMode::None;
  std::string actionId;
};

/// 角色在当前帧想要执行的意图，供状态机消费。
struct CharacterIntent {
  float moveX = 0.0f;
  float moveY = 0.0f;
  CharacterMoveMode moveMode = CharacterMoveMode::None;
  bool wantJump = false;
  std::vector<std::string> requestedActions;
};

/// 主状态机的大状态，普攻和技能统一走 Action。
enum class CharacterStateId {
  Idle,
  Move,
  Jump,
  Fall,
  Landing,
  Action,
  Hurt,
  Dead,
};

/// 角色的逻辑世界坐标。x/y 是地面平面，z 是高度轴。
struct CharacterWorldPosition {
  int32 x = 0;
  int32 y = 0;
  int32 z = 0;

  /// 当前项目的 2.5D 投影：地面 y 决定站位，高度 z 决定视觉抬升。
  Vec2 ToScreenPosition() const {
    return Vec2(static_cast<float>(x), static_cast<float>(y - z));
  }
  Vec2 ToGroundPosition() const {
    return Vec2(static_cast<float>(x), static_cast<float>(y));
  }
};

/// 运动器只负责推进坐标和速度，不直接决定状态流转。
struct CharacterMotor {
  CharacterWorldPosition position;
  Vec2 groundVelocity = Vec2::Zero();
  Vec2 forcedSlideVelocity = Vec2::Zero();
  float verticalVelocity = 0.0f;
  float forcedSlideRemainingTime = 0.0f;
  bool grounded = true;
  int facing = 1;
  float moveSpeed = 220.0f;
  float jumpSpeed = 520.0f;
  float gravity = 1600.0f;

  void SetGroundPosition(const Vec2& groundPosition) {
    position.x = gameMath::RoundWorldCoordinate(groundPosition.x);
    position.y = gameMath::RoundWorldCoordinate(groundPosition.y);
    positionRemainder_ = Vec2::Zero();
  }

  void SetWorldPosition(const CharacterWorldPosition& worldPosition) {
    position = worldPosition;
    ClearPositionRemainders();
  }

  void ApplyGroundInput(float moveX, float moveY, float speedScale = 1.0f) {
    Vec2 input(moveX, moveY);
    if (input.lengthSquared() > 1.0f) {
      input = input.normalized();
    }
    groundVelocity = input * (moveSpeed * speedScale);
    if (input.x > 0.01f) {
      facing = 1;
    } else if (input.x < -0.01f) {
      facing = -1;
    }
  }

  void StopGroundMovement() { groundVelocity = Vec2::Zero(); }
  void ClearForcedSlide() {
    forcedSlideVelocity = Vec2::Zero();
    forcedSlideRemainingTime = 0.0f;
  }

  void StartForcedSlide(const Vec2& direction, float distance, float duration) {
    if (distance <= 0.0f || duration <= 0.0f || direction.lengthSquared() < 0.0001f) {
      ClearForcedSlide();
      return;
    }

    forcedSlideVelocity = direction.normalized() * (distance / duration);
    forcedSlideRemainingTime = duration;
  }

  void Jump() {
    grounded = false;
    verticalVelocity = jumpSpeed;
    if (position.z < 0) {
      position.z = 0;
      verticalPositionRemainder_ = 0.0f;
    }
  }

  void Update(float deltaTime) {
    positionRemainder_.x += groundVelocity.x * deltaTime;
    positionRemainder_.y += groundVelocity.y * deltaTime;
    if (forcedSlideRemainingTime > 0.0f && forcedSlideVelocity.lengthSquared() > 0.0f) {
      float slideDeltaTime = deltaTime < forcedSlideRemainingTime
                           ? deltaTime
                           : forcedSlideRemainingTime;
      positionRemainder_.x += forcedSlideVelocity.x * slideDeltaTime;
      positionRemainder_.y += forcedSlideVelocity.y * slideDeltaTime;
      forcedSlideRemainingTime -= slideDeltaTime;
      if (forcedSlideRemainingTime <= 0.0f) {
        ClearForcedSlide();
      }
    }
    position.x += ConsumeWholeUnits(positionRemainder_.x);
    position.y += ConsumeWholeUnits(positionRemainder_.y);

    if (!grounded || position.z > 0 || verticalVelocity > 0.0f) {
      verticalPositionRemainder_ += verticalVelocity * deltaTime;
      verticalVelocity -= gravity * deltaTime;
      grounded = false;
      position.z += ConsumeWholeUnits(verticalPositionRemainder_);

      if (position.z <= 0) {
        position.z = 0;
        verticalVelocity = 0.0f;
        verticalPositionRemainder_ = 0.0f;
        grounded = true;
      }
    } else {
      position.z = 0;
      verticalVelocity = 0.0f;
      verticalPositionRemainder_ = 0.0f;
      grounded = true;
    }
  }

private:
  static int32 ConsumeWholeUnits(float& remainder) {
    if (remainder >= 1.0f) {
      int32 wholeUnits = static_cast<int32>(std::floor(remainder));
      remainder -= static_cast<float>(wholeUnits);
      return wholeUnits;
    }
    if (remainder <= -1.0f) {
      int32 wholeUnits = static_cast<int32>(std::ceil(remainder));
      remainder -= static_cast<float>(wholeUnits);
      return wholeUnits;
    }
    return 0;
  }

  void ClearPositionRemainders() {
    positionRemainder_ = Vec2::Zero();
    verticalPositionRemainder_ = 0.0f;
  }

  Vec2 positionRemainder_ = Vec2::Zero();
  float verticalPositionRemainder_ = 0.0f;
};

/// 逻辑动作定义。这里只保留显式注册的逻辑入口名。
///
/// 动画播放统一由状态类/技能类直接调用 `PlayAnimationTag()` 控制，
/// 不再通过动作定义反查 animationTag。
struct CharacterActionDefinition {
  std::string actionId;
};

using CharacterRuntimeValue = std::variant<bool, int, float, std::string, Vec2>;

class CharacterRuntimeBlackboard {
public:
  void SetBool(const std::string& key, bool value);
  void SetInt(const std::string& key, int value);
  void SetFloat(const std::string& key, float value);
  void SetString(const std::string& key, const std::string& value);
  void SetVec2(const std::string& key, const Vec2& value);

  bool TryGetBool(const std::string& key, bool& outValue) const;
  bool TryGetInt(const std::string& key, int& outValue) const;
  bool TryGetFloat(const std::string& key, float& outValue) const;
  bool TryGetString(const std::string& key, std::string& outValue) const;
  bool TryGetVec2(const std::string& key, Vec2& outValue) const;

  bool Has(const std::string& key) const;
  void Remove(const std::string& key);
  void Clear();

private:
  template <typename T>
  void SetValue(const std::string& key, T&& value) {
    values_[key] = std::forward<T>(value);
  }

  template <typename T>
  bool TryGetValue(const std::string& key, T& outValue) const {
    auto it = values_.find(key);
    if (it == values_.end()) {
      return false;
    }

    const T* value = std::get_if<T>(&it->second);
    if (!value) {
      return false;
    }

    outValue = *value;
    return true;
  }

  std::unordered_map<std::string, CharacterRuntimeValue> values_;
};

struct CharacterActionContextData {
  bool valid = false;
  std::string requestedActionId;
  std::string sourceActionId;
  CharacterStateId sourceStateId = CharacterStateId::Idle;
  bool hasSourceStateId = false;
  CharacterRuntimeBlackboard values;

  void Clear() {
    valid = false;
    requestedActionId.clear();
    sourceActionId.clear();
    sourceStateId = CharacterStateId::Idle;
    hasSourceStateId = false;
    values.Clear();
  }

  void SetSourceStateId(CharacterStateId stateId) {
    sourceStateId = stateId;
    hasSourceStateId = true;
  }

  void SetBool(const std::string& key, bool value) { values.SetBool(key, value); }
  void SetInt(const std::string& key, int value) { values.SetInt(key, value); }
  void SetFloat(const std::string& key, float value) { values.SetFloat(key, value); }
  void SetString(const std::string& key, const std::string& value) {
    values.SetString(key, value);
  }
  void SetVec2(const std::string& key, const Vec2& value) { values.SetVec2(key, value); }

  bool TryGetBool(const std::string& key, bool& outValue) const {
    return values.TryGetBool(key, outValue);
  }
  bool TryGetInt(const std::string& key, int& outValue) const {
    return values.TryGetInt(key, outValue);
  }
  bool TryGetFloat(const std::string& key, float& outValue) const {
    return values.TryGetFloat(key, outValue);
  }
  bool TryGetString(const std::string& key, std::string& outValue) const {
    return values.TryGetString(key, outValue);
  }
  bool TryGetVec2(const std::string& key, Vec2& outValue) const {
    return values.TryGetVec2(key, outValue);
  }

  bool Has(const std::string& key) const { return values.Has(key); }
  void Remove(const std::string& key) { values.Remove(key); }
};

inline void CharacterRuntimeBlackboard::SetBool(const std::string& key, bool value) {
  SetValue(key, value);
}

inline void CharacterRuntimeBlackboard::SetInt(const std::string& key, int value) {
  SetValue(key, value);
}

inline void CharacterRuntimeBlackboard::SetFloat(const std::string& key, float value) {
  SetValue(key, value);
}

inline void CharacterRuntimeBlackboard::SetString(const std::string& key,
                                                  const std::string& value) {
  SetValue(key, value);
}

inline void CharacterRuntimeBlackboard::SetVec2(const std::string& key, const Vec2& value) {
  SetValue(key, value);
}

inline bool CharacterRuntimeBlackboard::TryGetBool(const std::string& key,
                                                   bool& outValue) const {
  return TryGetValue(key, outValue);
}

inline bool CharacterRuntimeBlackboard::TryGetInt(const std::string& key, int& outValue) const {
  return TryGetValue(key, outValue);
}

inline bool CharacterRuntimeBlackboard::TryGetFloat(const std::string& key,
                                                    float& outValue) const {
  return TryGetValue(key, outValue);
}

inline bool CharacterRuntimeBlackboard::TryGetString(const std::string& key,
                                                     std::string& outValue) const {
  return TryGetValue(key, outValue);
}

inline bool CharacterRuntimeBlackboard::TryGetVec2(const std::string& key,
                                                   Vec2& outValue) const {
  return TryGetValue(key, outValue);
}

inline bool CharacterRuntimeBlackboard::Has(const std::string& key) const {
  return values_.find(key) != values_.end();
}

inline void CharacterRuntimeBlackboard::Remove(const std::string& key) {
  values_.erase(key);
}

inline void CharacterRuntimeBlackboard::Clear() {
  values_.clear();
}

/// 运行期状态标记，承载霸体、无敌等战斗属性。
struct CharacterStatus {
  bool superArmor = false;
  bool invincible = false;
  bool controlLocked = false;
};

/// 外部战斗系统打入角色时携带的受击上下文。
struct HitContext {
  std::string hurtAction = "hurt_light";
  float launchZVelocity = 0.0f;
  bool ignoreInterrupt = false;
};

/// 短时缓存输入，提供预输入和连段窗口的基础能力。
class CharacterCommandBuffer {
public:
  void Advance(float deltaTime);
  void Submit(const CharacterCommand& command);
  CharacterIntent BuildIntent() const;
  bool HasBuffered(CharacterCommandType type) const;
  void Consume(CharacterCommandType type);
  bool HasBufferedAction(const std::string& actionId) const;
  void ConsumeAction(const std::string& actionId);
  void ClearBufferedActions();
  void Clear();

private:
  struct BufferedButton {
    CharacterCommandType type = CharacterCommandType::ActionPressed;
    std::string actionId;
    float age = 0.0f;
  };

  Vec2 moveAxis_ = Vec2::Zero();
  CharacterMoveMode moveMode_ = CharacterMoveMode::None;
  std::vector<BufferedButton> buttons_;
  float bufferWindowSeconds_ = 0.2f;
};

} // namespace frostbite2D
