package com.openosrs.client.api;

import com.openosrs.client.core.ClientCore;
import com.openosrs.client.core.EventSystem;
import com.openosrs.client.api.types.Position;
import com.openosrs.client.api.types.InventoryChange;
import com.openosrs.client.api.types.ExperienceGain;
import com.openosrs.client.api.types.CombatStateChange;
import com.openosrs.client.api.types.NPC;
import com.openosrs.client.api.types.ChatMessage;
import com.openosrs.client.api.types.HealthInfo;
import com.openosrs.client.api.types.Skill; // TODO: There is no implementation for Skill
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.function.Consumer;

/**
 * EventAPI - Provides high-level event registration for agents.
 *
 * This API module provides convenient methods for agents to register
 * for various game events without dealing with the low-level event system.
 */
public class EventAPI {
    private static final Logger logger = LoggerFactory.getLogger(EventAPI.class);

    private final ClientCore clientCore;
    private final EventSystem eventSystem;

    public EventAPI(ClientCore clientCore) {
        this.clientCore = clientCore;
        this.eventSystem = clientCore.getEventSystem();
    }

    /**
     * Register a listener for player movement events.
     */
    public void onPlayerMoved(Consumer<Position> listener) {
        eventSystem.addListener(EventSystem.EventType.PLAYER_MOVED, event -> {
            if (event instanceof EventSystem.PlayerMovedEvent) {
                EventSystem.PlayerMovedEvent moveEvent = (EventSystem.PlayerMovedEvent) event;
                Position position = new Position(moveEvent.getX(), moveEvent.getY(), moveEvent.getPlane());
                listener.accept(position);
            }
        });
        logger.debug("Registered player movement listener");
    }

    /**
     * Register a listener for health changes.
     */
    public void onHealthChanged(Consumer<HealthInfo> listener) {
        eventSystem.addListener(EventSystem.EventType.HEALTH_CHANGED, event -> {
            if (event instanceof EventSystem.HealthChangedEvent) {
                EventSystem.HealthChangedEvent healthEvent = (EventSystem.HealthChangedEvent) event;
                HealthInfo healthInfo = new HealthInfo(healthEvent.getCurrent(), healthEvent.getMax());
                listener.accept(healthInfo);
            }
        });
        logger.debug("Registered health change listener");
    }

    /**
     * Register a listener for inventory changes.
     */
    public void onInventoryChanged(Consumer<InventoryChange> listener) {
        eventSystem.addListener(EventSystem.EventType.INVENTORY_CHANGED, event -> {
            if (event instanceof EventSystem.InventoryChangedApiEvent) {
                EventSystem.InventoryChangedApiEvent invEvent = (EventSystem.InventoryChangedApiEvent) event;
                listener.accept(invEvent.getChange());
            }
        });
        logger.debug("Registered inventory change listener");
    }

    /**
     * Register a listener for experience gains.
     */
    public void onExperienceGained(Consumer<ExperienceGain> listener) {
        eventSystem.addListener(EventSystem.EventType.EXPERIENCE_CHANGED, event -> {
            if (event instanceof EventSystem.ExperienceChangedEvent) {
                EventSystem.ExperienceChangedEvent expEvent = (EventSystem.ExperienceChangedEvent) event;

                // Convert skill ID to Skill enum
                Skill skill = null;
                for (Skill s : Skill.values()) {
                    if (s.getId() == expEvent.getSkill()) {
                        skill = s;
                        break;
                    }
                }

                if (skill != null) {
                    // We need to track old experience to create ExperienceGain
                    // For now, assume 0 as old experience (this should be improved)
                    ExperienceGain gain = new ExperienceGain(skill, 0, expEvent.getExperience());
                    listener.accept(gain);
                }
            }
        });
        logger.debug("Registered experience gain listener");
    }

    /**
     * Register a listener for combat state changes.
     */
    public void onCombatStateChanged(Consumer<CombatStateChange> listener) {
        eventSystem.addListener(EventSystem.EventType.COMBAT_STATE_CHANGED, event -> {
            if (event instanceof EventSystem.CombatStateChangedEvent) {
                EventSystem.CombatStateChangedEvent combatEvent = (EventSystem.CombatStateChangedEvent) event;

                // TODO: Convert target ID to NPC object when we have NPC tracking
                NPC target = null; // Would need to look up NPC by target ID

                CombatStateChange stateChange = new CombatStateChange(combatEvent.isInCombat(), target);
                listener.accept(stateChange);
            }
        });
        logger.debug("Registered combat state change listener");
    }

    /**
     * Register a listener for chat messages.
     */
    public void onChatMessage(Consumer<ChatMessage> listener) {
        eventSystem.addListener(EventSystem.EventType.CHAT_MESSAGE, event -> {
            if (event instanceof EventSystem.ChatMessageEvent) {
                EventSystem.ChatMessageEvent chatEvent = (EventSystem.ChatMessageEvent) event;
                ChatMessage message = new ChatMessage(chatEvent.getUsername(), chatEvent.getMessage(), chatEvent.getType());
                listener.accept(message);
            }
        });
        logger.debug("Registered chat message listener");
    }

    /**
     * Register a listener for game ticks.
     */
    public void onGameTick(Consumer<Long> listener) {
        eventSystem.addListener(EventSystem.EventType.GAME_TICK, event -> {
            listener.accept(event.getTimestamp());
        });
        logger.debug("Registered game tick listener");
    }

    /**
     * Register a listener for prayer changes.
     */
    public void onPrayerChanged(Consumer<HealthInfo> listener) {
        eventSystem.addListener(EventSystem.EventType.PRAYER_CHANGED, event -> {
            if (event instanceof EventSystem.PrayerChangedEvent) {
                EventSystem.PrayerChangedEvent prayerEvent = (EventSystem.PrayerChangedEvent) event;
                HealthInfo prayerInfo = new HealthInfo(prayerEvent.getCurrent(), prayerEvent.getMax());
                listener.accept(prayerInfo);
            }
        });
        logger.debug("Registered prayer change listener");
    }

    /**
     * Register a listener for run energy changes.
     */
    public void onRunEnergyChanged(Consumer<Integer> listener) {
        eventSystem.addListener(EventSystem.EventType.RUN_ENERGY_CHANGED, event -> {
            if (event instanceof EventSystem.RunEnergyChangedEvent) {
                EventSystem.RunEnergyChangedEvent energyEvent = (EventSystem.RunEnergyChangedEvent) event;
                listener.accept(energyEvent.getEnergy());
            }
        });
        logger.debug("Registered run energy change listener");
    }

    /**
     * Register a listener for skill level changes.
     */
    public void onSkillChanged(Consumer<SkillChange> listener) {
        eventSystem.addListener(EventSystem.EventType.SKILL_CHANGED, event -> {
            if (event instanceof EventSystem.SkillChangedEvent) {
                EventSystem.SkillChangedEvent skillEvent = (EventSystem.SkillChangedEvent) event;

                // Convert skill ID to Skill enum
                Skill skill = null;
                for (Skill s : Skill.values()) {
                    if (s.getId() == skillEvent.getSkill()) {
                        skill = s;
                        break;
                    }
                }

                if (skill != null) {
                    SkillChange change = new SkillChange(skill, skillEvent.getLevel(), skillEvent.getExperience());
                    listener.accept(change);
                }
            }
        });
        logger.debug("Registered skill change listener");
    }

    /**
     * Register a listener for animation changes.
     */
    public void onAnimationChanged(Consumer<Integer> listener) {
        eventSystem.addListener(EventSystem.EventType.ANIMATION_CHANGED, event -> {
            if (event instanceof EventSystem.AnimationChangedEvent) {
                EventSystem.AnimationChangedEvent animEvent = (EventSystem.AnimationChangedEvent) event;
                listener.accept(animEvent.getAnimationId());
            }
        });
        logger.debug("Registered animation change listener");
    }

    /**
     * Register a listener for interface state changes.
     */
    public void onInterfaceChanged(Consumer<InterfaceChange> listener) {
        eventSystem.addListener(EventSystem.EventType.INTERFACE_OPENED, event -> {
            if (event instanceof EventSystem.InterfaceOpenedEvent) {
                EventSystem.InterfaceOpenedEvent interfaceEvent = (EventSystem.InterfaceOpenedEvent) event;
                InterfaceChange change = new InterfaceChange(-1, interfaceEvent.getInterfaceId(), true);
                listener.accept(change);
            }
        });

        eventSystem.addListener(EventSystem.EventType.INTERFACE_CLOSED, event -> {
            if (event instanceof EventSystem.InterfaceClosedEvent) {
                EventSystem.InterfaceClosedEvent interfaceEvent = (EventSystem.InterfaceClosedEvent) event;
                InterfaceChange change = new InterfaceChange(interfaceEvent.getInterfaceId(), -1, false);
                listener.accept(change);
            }
        });
        logger.debug("Registered interface change listener");
    }

    /**
     * Register a listener for network connection changes.
     */
    public void onConnectionChanged(Consumer<Boolean> listener) {
        eventSystem.addListener(EventSystem.EventType.CONNECTION_LOST, event -> {
            listener.accept(false);
        });

        eventSystem.addListener(EventSystem.EventType.CONNECTION_RESTORED, event -> {
            listener.accept(true);
        });
        logger.debug("Registered connection change listener");
    }

    /**
     * Helper class for skill changes.
     */
    public static class SkillChange {
        private final Skill skill;
        private final int level;
        private final int experience;

        public SkillChange(Skill skill, int level, int experience) {
            this.skill = skill;
            this.level = level;
            this.experience = experience;
        }

        public Skill getSkill() { return skill; } // TODO: There is no implementation for Skill
        public int getLevel() { return level; }
        public int getExperience() { return experience; }

        @Override
        public String toString() {
            return String.format("SkillChange{skill=%s, level=%d, experience=%d}", skill, level, experience);
        }
    }

    /**
     * Helper class for interface changes.
     */
    public static class InterfaceChange {
        private final int oldInterface;
        private final int newInterface;
        private final boolean opened;

        public InterfaceChange(int oldInterface, int newInterface, boolean opened) {
            this.oldInterface = oldInterface;
            this.newInterface = newInterface;
            this.opened = opened;
        }

        public int getOldInterface() { return oldInterface; }
        public int getNewInterface() { return newInterface; }
        public boolean isOpened() { return opened; }

        @Override
        public String toString() {
            return String.format("InterfaceChange{old=%d, new=%d, opened=%s}", oldInterface, newInterface, opened);
        }
    }
}
