package com.openosrs.client.core;

import com.openosrs.client.api.types.Position;
import com.openosrs.client.api.types.Skill;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * PlayerState - Manages the current player's state and statistics.
 * 
 * This class provides agents with comprehensive access to:
 * - Player position and movement
 * - Health, prayer, and run energy
 * - Skill levels and experience
 * - Combat state and target
 * - Animation and movement state
 */
public class PlayerState {
    private static final Logger logger = LoggerFactory.getLogger(PlayerState.class);
    
    private final EventSystem eventSystem;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    
    // Position and movement
    private final AtomicReference<Position> position = new AtomicReference<>(new Position(3200, 3200, 0));
    private final AtomicInteger localX = new AtomicInteger(0);
    private final AtomicInteger localY = new AtomicInteger(0);
    private final AtomicBoolean moving = new AtomicBoolean(false);
    
    // Player stats
    private final AtomicInteger hitpoints = new AtomicInteger(10);
    private final AtomicInteger maxHitpoints = new AtomicInteger(10);
    private final AtomicInteger prayer = new AtomicInteger(1);
    private final AtomicInteger maxPrayer = new AtomicInteger(1);
    private final AtomicInteger runEnergy = new AtomicInteger(100);
    private final AtomicInteger combatLevel = new AtomicInteger(3);
    
    // Skills (indexed by skill ordinal)
    private final int[] skillLevels = new int[Skill.values().length];
    private final int[] skillExperience = new int[Skill.values().length];
    private final int[] skillBoostedLevels = new int[Skill.values().length];
    private final int[] skillRealLevels = new int[Skill.values().length];
    
    // Player state
    private final AtomicReference<String> username = new AtomicReference<>("");
    private final AtomicInteger animationId = new AtomicInteger(-1);
    private final AtomicInteger graphicId = new AtomicInteger(-1);
    private final AtomicInteger interacting = new AtomicInteger(-1);
    private final AtomicReference<String> overhead = new AtomicReference<>("");
    
    // Combat state
    private final AtomicInteger target = new AtomicInteger(-1);
    private final AtomicInteger inCombatTicks = new AtomicInteger(0);
    
    public PlayerState(EventSystem eventSystem) {
        this.eventSystem = eventSystem;
        initializeSkills();
    }
    
    private void initializeSkills() {
        // Initialize all skills to level 1 with appropriate experience
        for (Skill skill : Skill.values()) {
            int i = skill.ordinal();
            if (skill == Skill.HITPOINTS) { // Hitpoints starts at level 10
                skillLevels[i] = 10;
                skillRealLevels[i] = 10;
                skillBoostedLevels[i] = 10;
                skillExperience[i] = 1154; // Experience for level 10
            } else {
                skillLevels[i] = 1;
                skillRealLevels[i] = 1;
                skillBoostedLevels[i] = 1;
                skillExperience[i] = 0;
            }
        }
    }
    
    public void initialize() {
        logger.debug("Initializing PlayerState");
        // Set default position (Tutorial Island or Lumbridge)
        setPosition(new Position(3200, 3200, 0));
    }
    
    public void shutdown() {
        logger.debug("Shutting down PlayerState");
        // Clear sensitive data
        username.set("");
    }
    
    public void tick() {
        // Update combat timer
        if (inCombatTicks.get() > 0) {
            inCombatTicks.decrementAndGet();
        }
        
        // Update run energy (slowly regenerate)
        if (runEnergy.get() < 100) {
            // Regenerate 1 energy every 6 seconds (100 ticks)
            if (System.currentTimeMillis() % 100 == 0) {
                runEnergy.updateAndGet(energy -> Math.min(100, energy + 1));
            }
        }
    }
    
    // Position methods
    public void setPosition(Position newPosition) {
        if (newPosition == null) return;
        
        Position oldPosition = this.position.getAndSet(newPosition);
        if (!newPosition.equals(oldPosition)) {
            logger.debug("Player position changed to {}", newPosition);
            eventSystem.firePlayerMoved(newPosition.getX(), newPosition.getY(), newPosition.getPlane());
        }
    }
    
    public void setLocalPosition(int localX, int localY) {
        this.localX.set(localX);
        this.localY.set(localY);
    }
    
    public Position getPosition() { return position.get(); }
    public int getWorldX() { return position.get().getX(); }
    public int getWorldY() { return position.get().getY(); }
    public int getPlane() { return position.get().getPlane(); }
    public int getLocalX() { return localX.get(); }
    public int getLocalY() { return localY.get(); }
    
    // Movement state
    public void setMoving(boolean isMoving) {
        boolean oldMoving = this.moving.getAndSet(isMoving);
        if (oldMoving != isMoving) {
            eventSystem.fireMovementStateChanged(isMoving);
        }
    }
    
    public boolean isMoving() { return moving.get(); }
    
    // Health and prayer methods
    public void setHitpoints(int current, int max) {
        int oldCurrent = this.hitpoints.getAndSet(current);
        int oldMax = this.maxHitpoints.getAndSet(max);
        
        if (oldCurrent != current || oldMax != max) {
            eventSystem.fireHealthChanged(current, max);
        }
    }
    
    public void setPrayer(int current, int max) {
        int oldCurrent = this.prayer.getAndSet(current);
        int oldMax = this.maxPrayer.getAndSet(max);
        
        if (oldCurrent != current || oldMax != max) {
            eventSystem.firePrayerChanged(current, max);
        }
    }
    
    public void setRunEnergy(int energy) {
        int oldEnergy = this.runEnergy.getAndSet(Math.max(0, Math.min(100, energy)));
        if (oldEnergy != energy) {
            eventSystem.fireRunEnergyChanged(energy);
        }
    }
    
    public int getHitpoints() { return hitpoints.get(); }
    public int getMaxHitpoints() { return maxHitpoints.get(); }
    public int getPrayer() { return prayer.get(); }
    public int getMaxPrayer() { return maxPrayer.get(); }
    public int getRunEnergy() { return runEnergy.get(); }
    public int getCombatLevel() { return combatLevel.get(); }
    
    // Skill methods
    public void setSkillLevel(Skill skill, int level) {
        if (skill == null) return;
        
        lock.writeLock().lock();
        try {
            int skillIndex = skill.ordinal();
            int oldLevel = skillLevels[skillIndex];
            skillLevels[skillIndex] = level;
            skillBoostedLevels[skillIndex] = level; // Reset boosted level when real level changes
            
            if (oldLevel != level) {
                eventSystem.fireSkillChanged(skill.getIndex(), level, skillExperience[skillIndex]);
            }
        } finally {
            lock.writeLock().unlock();
        }
    }
    
    public void setSkillRealLevel(Skill skill, int realLevel) {
        if (skill == null) return;
        
        lock.writeLock().lock();
        try {
            int skillIndex = skill.ordinal();
            skillRealLevels[skillIndex] = realLevel;
        } finally {
            lock.writeLock().unlock();
        }
    }
    
    public void setSkillExperience(Skill skill, int experience) {
        if (skill == null) return;
        
        lock.writeLock().lock();
        try {
            int skillIndex = skill.ordinal();
            int oldExp = skillExperience[skillIndex];
            skillExperience[skillIndex] = experience;
            
            if (oldExp != experience) {
                eventSystem.fireExperienceChanged(skill.getIndex(), experience);
            }
        } finally {
            lock.writeLock().unlock();
        }
    }
    
    public void setBoostedSkillLevel(Skill skill, int boostedLevel) {
        if (skill == null) return;
        
        lock.writeLock().lock();
        try {
            int skillIndex = skill.ordinal();
            skillBoostedLevels[skillIndex] = boostedLevel;
        } finally {
            lock.writeLock().unlock();
        }
    }
    
    public int getSkillLevel(Skill skill) {
        if (skill == null) return 1;
        
        lock.readLock().lock();
        try {
            return skillLevels[skill.ordinal()];
        } finally {
            lock.readLock().unlock();
        }
    }
    
    public int getSkillRealLevel(Skill skill) {
        if (skill == null) return 1;
        
        lock.readLock().lock();
        try {
            return skillRealLevels[skill.ordinal()];
        } finally {
            lock.readLock().unlock();
        }
    }
    
    public int getSkillExperience(Skill skill) {
        if (skill == null) return 0;
        
        lock.readLock().lock();
        try {
            return skillExperience[skill.ordinal()];
        } finally {
            lock.readLock().unlock();
        }
    }
    
    public int getBoostedSkillLevel(Skill skill) {
        if (skill == null) return 1;
        
        lock.readLock().lock();
        try {
            return skillBoostedLevels[skill.ordinal()];
        } finally {
            lock.readLock().unlock();
        }
    }
    
    // Animation and interaction
    public void setCurrentAnimation(int animationId) {
        int oldAnimation = this.animationId.getAndSet(animationId);
        if (oldAnimation != animationId) {
            eventSystem.fireAnimationChanged(animationId);
        }
    }
    
    public void setGraphic(int graphicId) {
        this.graphicId.set(graphicId);
    }
    
    public void setInteracting(int targetIndex) {
        this.interacting.set(targetIndex);
    }
    
    public int getCurrentAnimation() { return animationId.get(); }
    public int getAnimationId() { return animationId.get(); }
    public int getGraphicId() { return graphicId.get(); }
    public int getInteracting() { return interacting.get(); }
    
    // Combat state
    public void enterCombat(int targetId) {
        this.target.set(targetId);
        this.inCombatTicks.set(100); // 1 minute of combat
        eventSystem.fireCombatStateChanged(true, targetId);
    }
    
    public void exitCombat() {
        int oldTarget = this.target.getAndSet(-1);
        this.inCombatTicks.set(0);
        if (oldTarget != -1) {
            eventSystem.fireCombatStateChanged(false, -1);
        }
    }
    
    public boolean isInCombat() {
        return inCombatTicks.get() > 0;
    }
    
    public int getCombatTarget() { return target.get(); }
    
    // Player identity
    public void setUsername(String username) {
        this.username.set(username != null ? username : "");
    }
    
    public String getUsername() { return username.get(); }
    
    public void setOverheadText(String text) {
        this.overhead.set(text != null ? text : "");
    }
    
    public String getOverheadText() { return overhead.get(); }
}