import 'dart:math' as math;

import 'package:wolf_3d_dart/src/entities/entities/enemies/enemy.dart';
import 'package:wolf_3d_dart/src/entities/entities/enemies/enemy_animation.dart';
import 'package:wolf_3d_dart/src/entities/entities/enemies/enemy_type.dart';
import 'package:wolf_3d_dart/src/entities/entity.dart';
import 'package:wolf_3d_dart/wolf_3d_data_types.dart';

class Dog extends Enemy {
  /// Original SPDDOG is 1024. 1 Tile is 65536 units.
  /// 1024 / 65536 = ~0.0156 tiles per tic.
  static const double speedPerTic = 0.0156;

  @override
  EnemyType get type => EnemyType.dog;

  Dog({
    required super.x,
    required super.y,
    required super.angle,
    required super.mapId,
  }) : super(
         spriteIndex: EnemyType.dog.animations.idle.start,
         state: EntityState.idle,
       ) {
    health = 1; // Dogs always have 1 HP in the original engine
  }

  @override
  ({Coordinate2D movement, double newAngle}) update({
    required int elapsedMs,
    required int elapsedDeltaMs,
    required Coordinate2D playerPosition,
    required bool Function(int x, int y) isWalkable,
    required void Function(int damage) onDamagePlayer,
    required void Function(int x, int y) tryOpenDoor,
  }) {
    Coordinate2D movement = const Coordinate2D(0, 0);
    double newAngle = angle;
    double distance = position.distanceTo(playerPosition);

    // 1. Perception
    checkWakeUp(
      elapsedMs: elapsedMs,
      playerPosition: playerPosition,
      isWalkable: isWalkable,
    );

    // 2. Discrete AI Decision Rhythm
    bool ticReady = processTics(elapsedDeltaMs, moveSpeed: 0);

    if (state == EntityState.attacking) {
      // --- AUTHENTIC T_Bite / Jump Sequence ---
      // Original Jump sequence (s_dogjump1 to s_dogjump5) is 5 frames, 10 tics each
      if (ticReady) {
        currentFrame++;

        if (currentFrame == 1) {
          // Phase 2: The actual bite (s_dogjump2)
          // Original hit chance is US_RndT() < 180 (~70% chance)
          if (distance <= 1.2 && math.Random().nextDouble() < (180 / 256)) {
            // Original damage: US_RndT() >> 4 (0 to 15 damage)
            int actualDamage = math.Random().nextInt(16);
            onDamagePlayer(actualDamage);
          }
          setTics(10);
        } else if (currentFrame < 5) {
          setTics(10); // Phases 3-5: Mid-air and Landing
        } else {
          // Sequence complete, return to chase
          state = EntityState.patrolling;
          currentFrame = 0;
          setTics(10);
        }
      }
    } else if (state != EntityState.dead) {
      // 3. Continuous Movement (T_DogChase / T_Path)
      double ticsThisFrame = elapsedDeltaMs / 14.28;
      double currentMoveSpeed = speedPerTic * ticsThisFrame;

      if (isAlerted) {
        newAngle = position.angleTo(playerPosition);

        // Trigger Jump: Original uses MINACTORDIST (approx 0.8 tiles)
        if (distance <= 0.8) {
          state = EntityState.attacking;
          currentFrame = 0;
          lastActionTime = elapsedMs;
          setTics(10);
          return (movement: const Coordinate2D(0, 0), newAngle: newAngle);
        }

        movement = getValidMovement(
          intendedMovement: Coordinate2D(
            math.cos(newAngle) * currentMoveSpeed,
            math.sin(newAngle) * currentMoveSpeed,
          ),
          playerPosition: playerPosition,
          isWalkable: isWalkable,
          tryOpenDoor: tryOpenDoor,
        );
      } else if (state == EntityState.patrolling) {
        movement = getValidMovement(
          intendedMovement: Coordinate2D(
            math.cos(angle) * currentMoveSpeed,
            math.sin(angle) * currentMoveSpeed,
          ),
          playerPosition: playerPosition,
          isWalkable: isWalkable,
          tryOpenDoor: tryOpenDoor,
        );
      }

      if (ticReady) {
        currentFrame = (currentFrame + 1) % 4;
        setTics(10); // Chase rhythm (s_dogchase1-4)
      }
    }

    _updateAnimation(elapsedMs, newAngle, playerPosition);
    return (movement: movement, newAngle: newAngle);
  }

  void _updateAnimation(
    int elapsedMs,
    double newAngle,
    Coordinate2D playerPosition,
  ) {
    double diff = position.angleTo(playerPosition) - newAngle;
    while (diff <= -math.pi) {
      diff += 2 * math.pi;
    }
    while (diff > math.pi) {
      diff -= 2 * math.pi;
    }

    EnemyAnimation currentAnim = switch (state) {
      EntityState.patrolling => EnemyAnimation.walking,
      EntityState.attacking => EnemyAnimation.attacking,
      EntityState.dead => isDying ? EnemyAnimation.dying : EnemyAnimation.dead,
      _ => EnemyAnimation.idle,
    };

    spriteIndex = type.getSpriteFromAnimation(
      animation: currentAnim,
      elapsedMs: elapsedMs,
      lastActionTime: lastActionTime,
      angleDiff: diff,
      walkFrameOverride: (state == EntityState.patrolling || isAlerted)
          ? currentFrame
          : null,
    );
  }
}
