import pygame
import pytmx
from client.resource_path import get_asset_path, get_resource_path
import math
from client.data import config
from importlib import import_module
from network.client import GameClient
from core.utils import get_map_id_from_filename
from client.ui.character_status_window import CharacterStatusWindow
from client.ui.game_menu import GameMenu, CountdownPopup
from client.ui.inventory_window import InventoryWindow
from client.src.send_json import send_update_state, send_character_action
from client.src.portal_effect import PortalEffect
from client.src.portal import Portal, PortalManager
from client.src.enemy import Enemy, load_enemy_sprite
from client.src.enemy_manager import ClientEnemyManager
from client.src.networked_players import NetworkedPlayers
from client.src.camera import Camera
from client.src.audio import AudioManager

class GameLoop:
    def __init__(self, client, player, map_filename):
        print("[DEBUG] GameLoop __init__ called")
        print(f"[DEBUG] GameLoop created. Player starting at: ({player.x}, {player.y})")

        self.player = player
        #self.player.spawned = True
        self.client = client
        self.audio_manger = AudioManager()
        self.audio_manger.stop_music() # Stop menu music when entering game

        # Initialize inventory window
        self.font = pygame.font.SysFont(config.FONT_NAME, 24)
        self.inventory_window = InventoryWindow(self.font, self.client, game_loop=self)
        self.inventory_window.request_inventory()

        # Initialize enemy manager BEFORE setting message handler
        self.enemy_manager = ClientEnemyManager()
        self.damage_numbers = []
        self.hit_effects = []

        # Spawn initial enemies if player data includes them
        initial_enemies = getattr(player, 'enemies', [])
        if initial_enemies:
            print(f"[GameLoop] Spawning {len(initial_enemies)} initial enemies")
            for enemy_data in initial_enemies:
                self.enemy_manager.add_enemy(enemy_data)
        else:
            print("[GameLoop] No initial enemies in player data")

        self.client.on_message = self.handle_server_message
        print(f"[DEBUG] on_message overwritten: {self.client.on_message}")

        self.clock = pygame.time.Clock()
        self._movement_update_timer = 0.0  # seconds
        self.current_map = self.load_map(map_filename)
        self.portal_manager = PortalManager(Portal(config.SCREEN_WIDTH, config.SCREEN_HEIGHT), self.load_portals())
        self.current_map_id = get_map_id_from_filename(map_filename)

        self.char_id = self.player.id
        self.networked_players = NetworkedPlayers(my_char_id=self.player.id)
        
        # Define base resolution
        self.BASE_WIDTH = 1920
        self.BASE_HEIGHT = 1080
        
        # Load background image
        self.background_image = self.load_background_image(map_filename)
        
        # Load NPCs for this map
        try:
            map_module = import_module(f"client.src.maps.{map_filename.replace('.tmx', '')}")
            self.npc_group = map_module.get_map_npcs()
            for npc in self.npc_group:
                npc.player_ref = self.player
                npc.load_action_frames(npc.action)
                if npc.frames:
                    npc.image = npc.frames[npc.frame_index]
                    npc.rect = npc.image.get_rect(topleft=(npc.x, npc.y))
                else:
                    print(f"Warning: No frames loaded for NPC action {npc.action}")
        except ImportError as e:
            print(f"Failed to load map module for {map_filename}: {e}")
            self.npc_group = pygame.sprite.Group()

        self.status_window_open = False
        self.status_window = None
        self.show_inventory = False
        self.show_menu = False
        self.game_menu = None
        self.countdown_popup = None
        self.exiting_to_menu = False

        self.player.set_game(self)

        self.camera = Camera(
            self.current_map.width * self.current_map.tilewidth,
            self.current_map.height * self.current_map.tileheight
        )

    def load_map(self, map_filename):
        map_path = get_resource_path(f"client/assets/maps/{map_filename}")
        return pytmx.load_pygame(map_path)

    def load_background_image(self, map_filename):
        try:
            # Load image based on map filename (e.g., map1.tmx -> map1.png)
            image_path = get_resource_path(f"client/assets/maps/{map_filename.replace('.tmx', '')}/{map_filename.replace('.tmx', '.png')}")
            image = pygame.image.load(image_path).convert_alpha()
            # Scale to base resolution if not already 1920x1080
            if image.get_size() != (self.BASE_WIDTH, self.BASE_HEIGHT):
                image = pygame.transform.scale(image, (self.BASE_WIDTH, self.BASE_HEIGHT))
            return image
        except pygame.error as e:
            print(f"Failed to load background image for {map_filename}: {e}")
            # Fallback to a blank surface
            return pygame.Surface((self.BASE_WIDTH, self.BASE_HEIGHT), pygame.SRCALPHA)

    def load_portals(self):
        portals = []
        for obj in self.current_map.objects:
            if obj.name.lower() == "portal":
                radius = float(obj.properties.get("radius", 0.2))
                x = obj.x + getattr(obj, "width", 0) / 2
                y = obj.y + getattr(obj, "height", 0) / 2
                portals.append({
                    "x": x,
                    "y": y,
                    "radius": radius,
                })
        return portals

    def is_blocked(self, px, py, dy):
        tile_x = int(px // self.current_map.tilewidth)
        tile_y = int(py // self.current_map.tileheight)

        platform_layer_index = None
        for i, layer in enumerate(self.current_map.visible_layers):
            if isinstance(layer, pytmx.TiledTileLayer) and layer.name.lower() == "platforms":
                platform_layer_index = i
                layer_width, layer_height = layer.width, layer.height
                break

        if platform_layer_index is None:
            return False

        if tile_x < 0 or tile_x >= layer_width or tile_y < 0 or tile_y >= layer_height:
            return True

        gid = self.current_map.get_tile_gid(tile_x, tile_y, platform_layer_index)
        if gid == 0:
            return False

        props = self.current_map.get_tile_properties_by_gid(gid) or {}

        if props.get("solid", False):
            return True
        if props.get("platform", False) and dy > 0:
            return True

        return False
    
    def handle_server_message(self, message):
        """
        Handle all server messages related to players, enemies, and map state.
        """
        action = message.get("action")
        #print("[DEBUG-CLIENT] Incoming message:", message)

        if message.get("status") == "error":
            error_msg = message.get("message", "Unknown error")
            print(f"[Server Error] {error_msg}")
            return  # Don't try to process as a normal action
        
        if action in ["inventory_data", "item_moved", "item_equipped", 
                     "item_unequipped", "item_dropped", "item_added"]:
            data = message.get("data", {})
            self.inventory_window.handle_response(action, data)
            return
        
        elif action == "character_update":
            character_data = message.get("character", {})
            if character_data:
                # Update player stats
                self.player.stats = character_data.get('stats', {})
                print(f"[GameLoop] Character stats updated")
                return

        elif action == "map_snapshot":
            players = message.get("players", [])
            print(f"[DEBUG] Received map_snapshot with {len(players)} players: {players}")
            self.networked_players.handle_snapshot(players)
            print(f"[DEBUG] Current networked players: {list(self.networked_players.other_players.keys())}")

        elif action == "player_spawn":
            player_data = message.get("player")
            if not player_data:
                print("[WARNING] player_spawn received with no player data")
                return
            print(f"[DEBUG] player_spawn received: {player_data}")
            self.networked_players.handle_spawn(player_data)
            print(f"[DEBUG] Networked players after spawn: {list(self.networked_players.other_players.keys())}")

        elif action == "player_update":
            player_data = message.get("player")
            if not player_data:
                print("[WARNING] player_update received with no player data")
                return
            self.networked_players.handle_update(player_data)

        elif action == "player_despawn":
            char_id = message.get("char_id")
            if char_id is None:
                print("[WARNING] player_despawn received with no char_id")
                return
            self.networked_players.handle_despawn(char_id)
            print(f"[DEBUG] Networked players after despawn: {list(self.networked_players.other_players.keys())}")

        elif action == "enemy_spawn":
            # Single enemy spawned mid-game (includes full data with actions)
            enemy_data = message.get("data")
            if enemy_data:
                print(f"[DEBUG] enemy_spawn received: {enemy_data.get('name')} (ID: {enemy_data.get('id')})")
                self.enemy_manager.add_enemy(enemy_data)

        elif action == "enemy_death":
            # Enemy died
            enemy_id = message.get("data", {}).get("id")
            if enemy_id:
                print(f"[DEBUG] enemy_death received for ID: {enemy_id}")
                self.enemy_manager.remove_enemy(enemy_id)

        elif action == "enemy_update":
            # Batch position/state updates (lightweight data, no actions)
            enemies = message.get("data", [])
            if enemies:
                # Use update_enemy for each enemy in the batch
                for enemy_data in enemies:
                    self.enemy_manager.update_enemy(enemy_data)
        elif action == "attack_ok":
            attack_data = message.get("data", {})
            enemy_id = attack_data.get("enemy_id")
            damage = attack_data.get("damage")
            
            print(f"[Combat] Hit enemy {enemy_id} for {damage} damage!")
            
            enemy = self.enemy_manager.enemies.get(enemy_id)
            if enemy:
                from client.src.combat_effects import DamageNumber
                dmg_num = DamageNumber(enemy.x, enemy.y - 30, damage, is_crit=False)
                self.damage_numbers.append(dmg_num)

        elif action == "player_damaged":
            dmg_data = message.get("data", {})
            damage = dmg_data.get("damage")
            enemy_name = dmg_data.get("enemy_name", "Enemy")
            
            print(f"[Combat] {enemy_name} hit you for {damage} damage!")
            self.player.take_damage(damage)
            
            from client.src.combat_effects import DamageNumber
            dmg_num = DamageNumber(self.player.x, self.player.y - 30, damage, is_player=True)
            self.damage_numbers.append(dmg_num)
        
        elif action == "item_dropped_from_enemy":
            data = message.get("data", {})
            item = data.get("item", {})
            enemy_name = data.get("enemy_name", "Enemy")
            seed_data = item.get("seed_data", {})
            item_name = seed_data.get("name", "Item")
            
            print(f"[Loot] You received {item_name} from {enemy_name}!")

            if self.inventory_window.is_open:
                self.inventory_window.request_inventory()

        elif action == "inventory_full":
            data = message.get("data", {})
            message_text = data.get("message", "Inventory is full!")
            print(f"[!] {message_text}")
            self.show_notification(message_text, color=(255, 0, 0))

        elif action == "server_debug_hitbox":
            # Server-side debug hitbox data for comparison with client
            debug_data = message.get("data", {})
            print(f"[CLIENT_DEBUG] Received server_debug_hitbox message")
            print(f"[CLIENT_DEBUG] Server hitbox data: attack_rect={debug_data.get('attack_rect')}, enemy_rect={debug_data.get('enemy_rect')}")
            self.player.update_server_debug_data(debug_data)

        else:
            # Handle unexpected or debug-only messages
            print(f"[DEBUG] Unknown server action received: {action}")

    def _update_player_weapon(self):
        """Update player's equipped weapon sprite from inventory"""
        equipped_slots = self.inventory_window.equipped_slots
        weapon_id = equipped_slots.get("weapon_slot")
        
        if weapon_id:
            # Find the weapon in inventory
            for item in self.inventory_window.items.values():
                if item.id == weapon_id:
                    self.player.update_equipped_weapon({
                        "item_id": item.item_id,
                        "seed_data": {
                            "name": item.name,
                            "subtype": item.item_type,
                            "stats": item.stats  # Include stats with attack_range
                        }
                    })
                    return
        
        # No weapon equipped
        self.player.update_equipped_weapon(None)

    def loop(self, screen):
        self._setup_loop_variables(screen)
        self.client.send_json({"action": "request_map_state"})
        
        # Send initial position and direction to ensure server sync
        self.client.send_json({
            "action": "update_state",
            "data": {
                "position": (self.player.x, self.player.y),
                "direction": self.player.direction,
                "map_id": self.current_map_id
            }
        })

        print("[GameLoop] Starting main loop")
        running = True
        frame_count = 0

        while running:
            frame_count += 1
            if frame_count == 1:
                print("[GameLoop] First frame running")
            if frame_count == 60:
                print("[GameLoop] 60 frames completed")
            
                
            dt = self.clock.tick(config.FPS) / 1000.0
            result = self._handle_events()
            if result == "quit":
                print("[GameLoop] Quit event detected")
                running = False
            elif result == "menu":
                print("[GameLoop] Menu return detected")
                return "menu"
            update_result = self._update_all(dt)
            if update_result == "main_menu":
                print("[GameLoop] Main menu return from update")
                return "menu"
            self._draw_all(screen)
            
        print(f"[GameLoop] Exited after {frame_count} frames")
        return "quit"

    def _setup_loop_variables(self, screen):
        self.game_menu = GameMenu(screen)
        self.virtual_surface = pygame.Surface((self.BASE_WIDTH, self.BASE_HEIGHT), pygame.SRCALPHA)
        self._movement_update_timer = 0.0
        self._last_sent_pos = (self.player.x, self.player.y)

    def _handle_events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return "quit"
            
            # Handle inventory toggle before blocking input
            if event.type == pygame.KEYDOWN and event.key in (pygame.K_i, pygame.K_ESCAPE):
                # ESC should only close inventory
                if self.inventory_window.is_open:
                    if event.key in (pygame.K_i, pygame.K_ESCAPE):
                        self.inventory_window.toggle()
                    continue
                # I should open it (ESC does nothing when closed)
                elif event.key == pygame.K_i:
                    self.inventory_window.toggle()
                    continue

            if self.inventory_window.is_open:
                self.inventory_window.handle_event(event, self.player)

                # Allow movement keys even when inventory is open
                if event.type == pygame.KEYDOWN and event.key in (pygame.K_w, pygame.K_a, pygame.K_s, pygame.K_d):
                    self.player.handle_movement(event)
                elif event.type == pygame.KEYUP and event.key in (pygame.K_w, pygame.K_a, pygame.K_s, pygame.K_d):
                    self.player.stop_movement(event)

                # Block all other inputs (mouse clicks, abilities, etc.)
                continue

            if self.show_menu:
                result = self.game_menu.handle_event(event)
                if result == "return":
                    self.show_menu = False
                elif result == "main_menu":
                    self.exiting_to_menu = True
                    if self.countdown_popup is None:
                        self.countdown_popup = CountdownPopup(pygame.display.get_surface(), pygame.font.SysFont(config.FONT_NAME, int(self.BASE_HEIGHT * 0.04)), duration=5)
                        self.countdown_popup.start()
                    self.show_menu = False
                continue
            if self.exiting_to_menu:
                self.countdown_popup.handle_event(event)
                continue
            
            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:  # Left click
                    mouse_pos = pygame.mouse.get_pos()
                    # Convert screen coordinates to virtual surface coordinates
                    current_screen = pygame.display.get_surface()
                    screen_width, screen_height = current_screen.get_size()
                    base_aspect = self.BASE_WIDTH / self.BASE_HEIGHT
                    screen_aspect = screen_width / screen_height
                    
                    if screen_aspect > base_aspect:
                        scaled_height = screen_height
                        scaled_width = int(screen_height * base_aspect)
                        offset_x = (screen_width - scaled_width) // 2
                        offset_y = 0
                    else:
                        scaled_width = screen_width
                        scaled_height = int(screen_width / base_aspect)
                        offset_x = 0
                        offset_y = (screen_height - scaled_height) // 2
                    
                    # Convert to virtual coordinates
                    virtual_x = int((mouse_pos[0] - offset_x) * self.BASE_WIDTH / scaled_width)
                    virtual_y = int((mouse_pos[1] - offset_y) * self.BASE_HEIGHT / scaled_height)
                    
                    # Add camera offset
                    cam_x, cam_y = self.camera.offset
                    world_pos = (virtual_x + cam_x, virtual_y + cam_y)
                    
                    # Check NPC clicks
                    for npc in self.npc_group:
                        if npc.is_clicked(world_pos):
                            npc.handle_click()
                            break
            
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_z:
                    target_id = self.player.get_nearby_loot_target()
                    if target_id:
                        send_character_action(self.client, "loot", {"target_id": target_id})
                elif event.key == pygame.K_o:
                    self.status_window_open = not self.status_window_open
                    if self.status_window_open:
                        self.status_window = CharacterStatusWindow(self.player)
                elif event.key == pygame.K_i:
                    self.inventory_window.toggle()

                elif event.key == pygame.K_ESCAPE:
                    if self.status_window_open:
                        self.status_window_open = False
                    elif self.show_inventory:
                        self.show_inventory = False
                    else:
                        self.show_menu = not self.show_menu
                elif event.key == pygame.K_x:  # X key to attack
                    target_id = self.player.get_nearest_enemy_in_range(
                        self.enemy_manager, 
                        attack_range=100
                    )
                    if target_id:
                        from client.src.send_json import send_attack
                        # Include current position and direction for server sync debugging
                        send_attack(self.client, target_id, 
                                  player_pos=(int(self.player.x), int(self.player.y)),
                                  player_direction=self.player.direction)
                    else:
                        print("[Player] No enemy in range")
        if not self.show_menu and not self.exiting_to_menu:
            keys = pygame.key.get_pressed()
            self.player.handle_input(keys)

        return None

    def _update_all(self, dt):
        self._movement_update_timer += dt
        current_pos = (self.player.x, self.player.y)
        current_direction = self.player.direction
        #print(f"[DEBUG] Player Position: ({int(self.player.x)}, {int(self.player.y)})")
        if self._movement_update_timer >= 0.1 and (current_pos != self._last_sent_pos or not hasattr(self, '_last_sent_direction') or current_direction != self._last_sent_direction):
            self._movement_update_timer = 0.0
            self._last_sent_pos = current_pos
            self._last_sent_direction = current_direction
            self.client.send_json({
                "action": "update_state",
                "data": {
                    "position": current_pos,
                    "direction": current_direction,
                    "map_id": self.current_map_id
                }
            })
        self.portal_manager.update(dt)
        self.npc_group.update(dt * 1000)
        self.enemy_manager.update(dt)
        self.networked_players.update(dt)
        if self.exiting_to_menu and self.countdown_popup:
            if self.countdown_popup.cancelled:
                self.exiting_to_menu = False
                self.countdown_popup = None
            elif self.countdown_popup.get_remaining() == 0:
                return "main_menu"
        # Update combat effects
        self.damage_numbers = [dn for dn in self.damage_numbers if dn.update(dt)]
        self.hit_effects = [he for he in self.hit_effects if he.update(dt)]

    def _draw_all(self, screen):
        self.virtual_surface.fill((0, 0, 0, 0))
        
        # Draw background image
        self.virtual_surface.blit(self.background_image, (0, 0))

        self.camera.update(self.player)
        cam_x, cam_y = self.camera.offset

        # Draw non-background Tiled layers
        for layer in self.current_map.visible_layers:
            if isinstance(layer, pytmx.TiledTileLayer):
                parallax = 1.0 if "midground" in layer.name.lower() else 1.0
                parallax_cam_x = int(cam_x * parallax)
                parallax_cam_y = int(cam_y * parallax)
                for x, y, gid in layer:
                    tile = self.current_map.get_tile_image_by_gid(gid)
                    if tile:
                        self.virtual_surface.blit(tile, (x * self.current_map.tilewidth - parallax_cam_x, y * self.current_map.tileheight - parallax_cam_y))

        # Draw combat effects
        for dmg_num in self.damage_numbers:
            dmg_num.draw(self.virtual_surface, cam_x, cam_y)
        for hit_effect in self.hit_effects:
            hit_effect.draw(self.virtual_surface, cam_x, cam_y)

        for npc in self.npc_group:
            self.virtual_surface.blit(npc.image, (npc.rect.x - cam_x, npc.rect.y - cam_y))
        
        self.enemy_manager.draw(self.virtual_surface, cam_x, cam_y)
        self.networked_players.draw(self.virtual_surface, cam_x, cam_y)
        if self.player.spawned:
            self.player.draw(self.virtual_surface, offset_x=-cam_x, offset_y=-cam_y)

        #for char_id, p in self.networked_players.other_players.items():
            #print(f"[DEBUG-DRAW] Drawing player {char_id} at ({int(p.x)}, {int(p.y)}) with state '{p.current_state}'")

        '''
        for p in self.networked_players.other_players.values():
            rect = pygame.Rect(
                int(p.x - cam_x + 80),
                int(p.y - cam_y + 80), 50, 50)
            pygame.draw.rect(self.virtual_surface, (255, 0, 0), rect, 2)
        '''

        #for char_id, p in self.networked_players.other_players.items():
           # print(f"[DEBUG-DRAW] Drawing player {char_id} at ({int(p.x)}, {int(p.y)})")

        for dmg_num in self.damage_numbers:
            dmg_num.draw(self.virtual_surface, cam_x, cam_y)

        self.portal_manager.draw(self.virtual_surface, cam_x, cam_y)

        if self.status_window_open:
            self.status_window.draw(self.virtual_surface)

        # Scale virtual surface to screen with aspect ratio preservation
        screen_width, screen_height = screen.get_size()
        base_aspect = self.BASE_WIDTH / self.BASE_HEIGHT
        screen_aspect = screen_width / screen_height

        if screen_aspect > base_aspect:
            # Screen is wider than base, fit to height
            scaled_height = screen_height
            scaled_width = int(screen_height * base_aspect)
            offset_x = (screen_width - scaled_width) // 2
            offset_y = 0
        else:
            # Screen is taller than base, fit to width
            scaled_width = screen_width
            scaled_height = int(screen_width / base_aspect)
            offset_x = 0
            offset_y = (screen_height - scaled_height) // 2

        scaled_surface = pygame.transform.scale(self.virtual_surface, (scaled_width, scaled_height))
        screen.fill((0, 0, 0))  # Clear screen with black to handle letterboxing
        screen.blit(scaled_surface, (offset_x, offset_y))

        if self.inventory_window.is_open:
            # Create a temporary player object with screen-space stats for drawing
            self.inventory_window.draw(screen, self.player)

        if self.show_menu:
            self.game_menu.draw()
        elif self.exiting_to_menu and self.countdown_popup:
            self.countdown_popup.draw()

        # Debug overlay for hitbox comparison
        if hasattr(self.player, 'debug_draw_hitbox') and self.player.debug_draw_hitbox:
            self._draw_debug_overlay(screen)

        pygame.display.flip()

    def _draw_debug_overlay(self, screen):
        """Draw debug information overlay"""
        try:
            font = pygame.font.Font(None, 24)
            small_font = pygame.font.Font(None, 20)
            
            # Background panel
            panel_width = 400
            panel_height = 200
            panel_x = screen.get_width() - panel_width - 10
            panel_y = 10
            
            panel_surface = pygame.Surface((panel_width, panel_height), pygame.SRCALPHA)
            panel_surface.fill((0, 0, 0, 180))  # Semi-transparent black
            screen.blit(panel_surface, (panel_x, panel_y))
            
            # Title
            title_text = font.render("DEBUG: Hitbox Comparison", True, (255, 255, 255))
            screen.blit(title_text, (panel_x + 10, panel_y + 10))
            
            y_offset = 40
            
            # Player position
            pos_text = small_font.render(f"Player Pos: ({int(self.player.x)}, {int(self.player.y)})", True, (255, 255, 255))
            screen.blit(pos_text, (panel_x + 10, panel_y + y_offset))
            y_offset += 25
            
            # Direction
            dir_text = small_font.render(f"Direction: {self.player.direction}", True, (255, 255, 255))
            screen.blit(dir_text, (panel_x + 10, panel_y + y_offset))
            y_offset += 25
            
            # Client hitbox info
            client_rect = self.player.get_attack_hitbox(attack_range=100)
            collision_rect = self.player.get_collision_center_attack_hitbox(attack_range=100)
            if client_rect:
                client_text = small_font.render(f"Client (Visual): ({client_rect.x}, {client_rect.y}, {client_rect.width}, {client_rect.height})", True, (255, 0, 0))
                screen.blit(client_text, (panel_x + 10, panel_y + y_offset))
                y_offset += 20
            
            if collision_rect:
                collision_text = small_font.render(f"Client (Collision): ({collision_rect.x}, {collision_rect.y}, {collision_rect.width}, {collision_rect.height})", True, (255, 128, 0))
                screen.blit(collision_text, (panel_x + 10, panel_y + y_offset))
                y_offset += 20
            
            # Server hitbox info
            if hasattr(self.player, 'server_debug_data') and self.player.server_debug_data:
                server_attack = self.player.server_debug_data.get('attack_rect', {})
                server_player_pos = self.player.server_debug_data.get('player_pos', {})
                client_pos = self.player.server_debug_data.get('client_pos', {})
                server_dir = self.player.server_debug_data.get('player_dir', 'unknown')
                client_dir = self.player.server_debug_data.get('client_dir', 'unknown')
                timestamp = self.player.server_debug_data.get('timestamp', 0)
                import time
                age_ms = int(time.time() * 1000) - timestamp
                
                # Show position mismatch
                if server_player_pos and client_pos:
                    pos_diff_x = server_player_pos['x'] - client_pos['x']
                    pos_diff_y = server_player_pos['y'] - client_pos['y']
                    pos_diff_text = small_font.render(f"Pos Diff: ({pos_diff_x}, {pos_diff_y})", True, (255, 255, 0))
                    screen.blit(pos_diff_text, (panel_x + 10, panel_y + y_offset))
                    y_offset += 20
                
                # Show direction mismatch
                if server_dir != client_dir:
                    dir_text = small_font.render(f"Dir Mismatch! Client: {client_dir}, Server: {server_dir}", True, (255, 0, 0))
                    screen.blit(dir_text, (panel_x + 10, panel_y + y_offset))
                    y_offset += 20
                
                # Show server data age
                age_text = small_font.render(f"Server data age: {age_ms}ms", True, (200, 200, 200))
                screen.blit(age_text, (panel_x + 10, panel_y + y_offset))
                y_offset += 20
                
                if server_attack:
                    server_text = small_font.render(f"Server: ({server_attack['x']}, {server_attack['y']}, {server_attack['w']}, {server_attack['h']})", True, (0, 0, 255))
                    screen.blit(server_text, (panel_x + 10, panel_y + y_offset))
                    y_offset += 20
                    
                    # Difference calculation (both visual and collision-based)
                    if client_rect and collision_rect:
                        # Visual vs Server
                        dx_visual = abs(client_rect.x - server_attack['x'])
                        dy_visual = abs(client_rect.y - server_attack['y'])
                        # Collision vs Server
                        dx_collision = abs(collision_rect.x - server_attack['x'])
                        dy_collision = abs(collision_rect.y - server_attack['y'])
                        
                        diff_visual_text = small_font.render(f"Visual diff: dx={dx_visual}, dy={dy_visual}", True, (255, 255, 0))
                        screen.blit(diff_visual_text, (panel_x + 10, panel_y + y_offset))
                        y_offset += 20
                        
                        diff_collision_text = small_font.render(f"Collision diff: dx={dx_collision}, dy={dy_collision}", True, (255, 200, 0))
                        screen.blit(diff_collision_text, (panel_x + 10, panel_y + y_offset))
                        y_offset += 20
                
                # Enemy hitbox info
                server_enemy = self.player.server_debug_data.get('enemy_rect', {})
                if server_enemy:
                    enemy_text = small_font.render(f"Enemy: ({server_enemy['x']}, {server_enemy['y']}, {server_enemy['w']}, {server_enemy['h']})", True, (0, 255, 0))
                    screen.blit(enemy_text, (panel_x + 10, panel_y + y_offset))
                    y_offset += 20
            else:
                no_server_text = small_font.render("Server: No data received yet", True, (128, 128, 128))
                screen.blit(no_server_text, (panel_x + 10, panel_y + y_offset))
                y_offset += 20
                
                # Show hint
                hint_text = small_font.render("Attack an enemy to see server data", True, (100, 100, 100))
                screen.blit(hint_text, (panel_x + 10, panel_y + y_offset))
                y_offset += 20
            
            # Legend
            legend_y = panel_y + panel_height - 30
            legend_text = small_font.render("RED=Visual, ORANGE=Collision, BLUE=Server, GREEN=Enemy", True, (200, 200, 200))
            screen.blit(legend_text, (panel_x + 10, legend_y))
            
        except Exception as e:
            print(f"[DEBUG] Error drawing debug overlay: {e}")