package com.openosrs.client.api;

import com.openosrs.client.core.ClientCore;
import com.openosrs.client.core.bridge.BridgeAdapter;
import com.openosrs.client.api.types.NPC;
import com.openosrs.client.api.types.GameObject;
import com.openosrs.client.api.types.GroundItem;
import com.openosrs.client.api.types.Position;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.stream.Collectors;

/**
 * WorldAPI - Provides access to world objects, NPCs, players, and ground items.
 *
 * This API module handles:
 * - NPCs (Non-Player Characters)
 * - Game Objects (doors, trees, rocks, etc.)
 * - Ground Items (items dropped on the ground)
 * - Other Players
 * - World state and information
 */
public class WorldAPI {
    private static final Logger logger = LoggerFactory.getLogger(WorldAPI.class);

    private final ClientCore clientCore;
    private final BridgeAdapter bridgeAdapter;

    public WorldAPI(ClientCore clientCore, BridgeAdapter bridgeAdapter) {
        this.clientCore = clientCore;
        this.bridgeAdapter = bridgeAdapter;
    }

    // === NPC METHODS ===

    /**
     * Get all NPCs currently visible.
     */
    public List<NPC> getNPCs() {
        return bridgeAdapter.getNPCs();
    }

    /**
     * Get NPCs by name.
     */
    public List<NPC> getNPCs(String name) {
        return getNPCs().stream()
                .filter(npc -> npc.getName().equalsIgnoreCase(name))
                .collect(Collectors.toList());
    }

    /**
     * Get NPCs by ID.
     */
    public List<NPC> getNPCs(int id) {
        return getNPCs().stream()
                .filter(npc -> npc.getId() == id)
                .collect(Collectors.toList());
    }

    /**
     * Get the closest NPC to the player.
     */
    public NPC getClosestNPC() {
        Position playerPos = bridgeAdapter.getPlayerPosition(); // TODO: Cannot convert type Object to type Position
        if (playerPos == null) {
            return null;
        }

        return getNPCs().stream()
                .min((a, b) -> Double.compare(
                    playerPos.distanceTo(a.getPosition()),
                    playerPos.distanceTo(b.getPosition())
                ))
                .orElse(null);
    }

    /**
     * Get the closest NPC with the specified name.
     */
    public NPC getClosestNPC(String name) {
        Position playerPos = bridgeAdapter.getPlayerPosition(); // TODO: Cannot convert type Object to type Position
        if (playerPos == null) {
            return null;
        }

        return getNPCs(name).stream()
                .min((a, b) -> Double.compare(
                    playerPos.distanceTo(a.getPosition()),
                    playerPos.distanceTo(b.getPosition())
                ))
                .orElse(null);
    }

    /**
     * Get the closest NPC with the specified ID.
     */
    public NPC getClosestNPC(int id) {
        Position playerPos = bridgeAdapter.getPlayerPosition(); // TODO: Cannot convert type Object to type Position
        if (playerPos == null) {
            return null;
        }

        return getNPCs(id).stream()
                .min((a, b) -> Double.compare(
                    playerPos.distanceTo(a.getPosition()),
                    playerPos.distanceTo(b.getPosition())
                ))
                .orElse(null);
    }

    /**
     * Get NPCs within a certain distance of the player.
     */
    public List<NPC> getNPCsWithinDistance(double maxDistance) {
        Position playerPos = bridgeAdapter.getPlayerPosition(); // TODO: Cannot convert type Object to type Position
        if (playerPos == null) {
            return List.of();
        }

        return getNPCs().stream()
                .filter(npc -> playerPos.distanceTo(npc.getPosition()) <= maxDistance)
                .collect(Collectors.toList());
    }

    /**
     * Get NPCs within a certain distance of a position.
     */
    public List<NPC> getNPCsWithinDistance(Position position, double maxDistance) {
        return getNPCs().stream()
                .filter(npc -> position.distanceTo(npc.getPosition()) <= maxDistance)
                .collect(Collectors.toList());
    }

    // === GAME OBJECT METHODS ===

    /**
     * Get all game objects currently visible.
     */
    public List<GameObject> getGameObjects() { // TODO: Type Mismatch between List<GameObject> and List<Object>
        return bridgeAdapter.getGameObjects();
    }

    /**
     * Get game objects by name.
     */
    public List<GameObject> getGameObjects(String name) {
        return getGameObjects().stream()
                .filter(obj -> obj.getName().equalsIgnoreCase(name))
                .collect(Collectors.toList());
    }

    /**
     * Get game objects by ID.
     */
    public List<GameObject> getGameObjects(int id) {
        return getGameObjects().stream()
                .filter(obj -> obj.getId() == id)
                .collect(Collectors.toList());
    }

    /**
     * Get the closest game object to the player.
     */
    public GameObject getClosestGameObject() {
        Position playerPos = bridgeAdapter.getPlayerPosition(); // TODO: Cannot convert type Object to type Position
        if (playerPos == null) {
            return null;
        }

        return getGameObjects().stream()
                .min((a, b) -> Double.compare(
                    playerPos.distanceTo(a.getPosition()),
                    playerPos.distanceTo(b.getPosition())
                ))
                .orElse(null);
    }

    /**
     * Get the closest game object with the specified name.
     */
    public GameObject getClosestGameObject(String name) {
        Position playerPos = bridgeAdapter.getPlayerPosition(); // TODO: Cannot convert type Object to type Position
        if (playerPos == null) {
            return null;
        }

        return getGameObjects(name).stream()
                .min((a, b) -> Double.compare(
                    playerPos.distanceTo(a.getPosition()),
                    playerPos.distanceTo(b.getPosition())
                ))
                .orElse(null);
    }

    /**
     * Get the closest game object with the specified ID.
     */
    public GameObject getClosestGameObject(int id) {
        Position playerPos = bridgeAdapter.getPlayerPosition(); // TODO: Cannot convert type Object to type Position
        if (playerPos == null) {
            return null;
        }

        return getGameObjects(id).stream()
                .min((a, b) -> Double.compare(
                    playerPos.distanceTo(a.getPosition()),
                    playerPos.distanceTo(b.getPosition())
                ))
                .orElse(null);
    }

    /**
     * Get game objects within a certain distance of the player.
     */
    public List<GameObject> getGameObjectsWithinDistance(double maxDistance) {
        Position playerPos = bridgeAdapter.getPlayerPosition(); // TODO: Cannot convert type Object to type Position
        if (playerPos == null) {
            return List.of();
        }

        return getGameObjects().stream()
                .filter(obj -> playerPos.distanceTo(obj.getPosition()) <= maxDistance)
                .collect(Collectors.toList());
    }

    /**
     * Get game objects within a certain distance of a position.
     */
    public List<GameObject> getGameObjectsWithinDistance(Position position, double maxDistance) {
        return getGameObjects().stream()
                .filter(obj -> position.distanceTo(obj.getPosition()) <= maxDistance)
                .collect(Collectors.toList());
    }

    // === GROUND ITEM METHODS ===

    /**
     * Get all ground items currently visible.
     */
    public List<GroundItem> getGroundItems() {
        return bridgeAdapter.getGroundItems(); // TODO: Type Mismatch between List<GroundItem> and List<Object>
    }

    /**
     * Get ground items by name.
     */
    public List<GroundItem> getGroundItems(String name) {
        return getGroundItems().stream()
                .filter(item -> item.getName().equalsIgnoreCase(name))
                .collect(Collectors.toList());
    }

    /**
     * Get ground items by ID.
     */
    public List<GroundItem> getGroundItems(int id) {
        return getGroundItems().stream()
                .filter(item -> item.getId() == id) // TODO: Fix getId() method in GroundItem
                .collect(Collectors.toList());
    }

    /**
     * Get the closest ground item to the player.
     */
    public GroundItem getClosestGroundItem() {
        Position playerPos = bridgeAdapter.getPlayerPosition(); // TODO: Cannot convert type Object to type Position
        if (playerPos == null) {
            return null;
        }

        return getGroundItems().stream()
                .min((a, b) -> Double.compare(
                    playerPos.distanceTo(a.getPosition()),
                    playerPos.distanceTo(b.getPosition())
                ))
                .orElse(null);
    }

    /**
     * Get the closest ground item with the specified name.
     */
    public GroundItem getClosestGroundItem(String name) {
        Position playerPos = bridgeAdapter.getPlayerPosition(); // TODO: Cannot convert type Object to type Position
        if (playerPos == null) {
            return null;
        }

        return getGroundItems(name).stream()
                .min((a, b) -> Double.compare(
                    playerPos.distanceTo(a.getPosition()),
                    playerPos.distanceTo(b.getPosition())
                ))
                .orElse(null);
    }

    /**
     * Get the closest ground item with the specified ID.
     */
    public GroundItem getClosestGroundItem(int id) {
        Position playerPos = bridgeAdapter.getPlayerPosition(); // TODO: Cannot convert type Object to type Position
        if (playerPos == null) {
            return null;
        }

        return getGroundItems(id).stream()
                .min((a, b) -> Double.compare(
                    playerPos.distanceTo(a.getPosition()),
                    playerPos.distanceTo(b.getPosition())
                ))
                .orElse(null);
    }

    /**
     * Get ground items within a certain distance of the player.
     */
    public List<GroundItem> getGroundItemsWithinDistance(double maxDistance) {
        Position playerPos = bridgeAdapter.getPlayerPosition(); // TODO: Cannot convert type Object to type Position
        if (playerPos == null) {
            return List.of();
        }

        return getGroundItems().stream()
                .filter(item -> playerPos.distanceTo(item.getPosition()) <= maxDistance)
                .collect(Collectors.toList());
    }

    /**
     * Get ground items within a certain distance of a position.
     */
    public List<GroundItem> getGroundItemsWithinDistance(Position position, double maxDistance) {
        return getGroundItems().stream()
                .filter(item -> position.distanceTo(item.getPosition()) <= maxDistance)
                .collect(Collectors.toList());
    }

    // === UTILITY METHODS ===

    /**
     * Check if a position is walkable.
     */
    public boolean isWalkable(Position position) {
        // This would need to check collision data from RuneLite
        // For now, return true as a placeholder
        return true;
    }

    /**
     * Get the current world number.
     */
    public int getWorldNumber() {
        // This would need to be retrieved from RuneLite
        // For now, return a placeholder
        return 301;
    }

    /**
     * Check if an area is loaded.
     */
    public boolean isAreaLoaded(Position position, int radius) {
        // This would need to check if the area around position is loaded
        // For now, assume it's loaded if the position is reasonable
        return position != null && position.getX() > 0 && position.getY() > 0;
    }
}
