"""
Server-Authoritative Movement and Anti-Speedhack Engine for FreeExile.
Calculates authoritative character displacement based on player move vectors,
detects speedhacking/teleporting, performs rubberband reconciliation, and manages suspicion scoring.
"""

import math
from typing import Dict, Tuple, Optional
from dataclasses import dataclass


@dataclass
class PlayerCharacter:
    entity_id: int
    x: float
    y: float
    move_speed: float = 6.0       # Base speed in meters per second
    suspicion_score: int = 0
    last_move_timestamp_ms: int = 0


class MovementAuthorityEngine:
    def __init__(self, max_base_speed: float = 6.0, tolerance_multiplier: float = 1.15):
        self.max_base_speed = max_base_speed
        self.tolerance_multiplier = tolerance_multiplier
        self.players: Dict[int, PlayerCharacter] = {}

    def register_player(self, player: PlayerCharacter) -> None:
        self.players[player.entity_id] = player

    def process_move_input(
        self,
        entity_id: int,
        dir_x: float,
        dir_y: float,
        dt: float
    ) -> Tuple[bool, float, float]:
        """
        Pure server-authoritative displacement:
        Computes final position directly on server from client's input vector.
        """
        player = self.players.get(entity_id)
        if not player or dt <= 0:
            return False, 0.0, 0.0

        # Normalize direction vector if not already normalized
        mag = math.hypot(dir_x, dir_y)
        if mag > 0.0001:
            norm_x = dir_x / mag
            norm_y = dir_y / mag
        else:
            norm_x, norm_y = 0.0, 0.0

        distance = player.move_speed * dt
        player.x += norm_x * distance
        player.y += norm_y * distance

        return True, player.x, player.y

    def validate_and_reconcile_position(
        self,
        entity_id: int,
        claimed_x: float,
        claimed_y: float,
        dt: float
    ) -> Tuple[bool, float, float]:
        """
        Validates client-reported coordinates against maximum permissible physical travel.
        If speedhack or invalid teleportation is detected, rubberbands back to server's true position.
        """
        player = self.players.get(entity_id)
        if not player or dt <= 0:
            return False, 0.0, 0.0

        traveled_dist = math.hypot(claimed_x - player.x, claimed_y - player.y)
        max_allowed_dist = (player.move_speed * dt) * self.tolerance_multiplier

        if traveled_dist > max_allowed_dist:
            # Speedhack / invalid teleport detected!
            player.suspicion_score += 10
            # Rubberband back to last known authoritative position
            return False, player.x, player.y

        # Valid movement step: update authoritative position
        player.x = claimed_x
        player.y = claimed_y
        return True, player.x, player.y
