package com.openosrs.client.login;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;

/**
 * LoginStateTracker - Monitors and tracks login state changes with detailed logging.
 * 
 * This class provides:
 * - Real-time login state monitoring
 * - State change event notifications
 * - Detailed login progress history
 * - Performance metrics and timing
 * - Error tracking and diagnostics
 * 
 * Designed to help AI agents understand and respond to login progress.
 */
public class LoginStateTracker {
    private static final Logger logger = LoggerFactory.getLogger(LoginStateTracker.class);
    
    private volatile LoginState currentState = LoginState.DISCONNECTED;
    private final List<StateTransition> stateHistory = new CopyOnWriteArrayList<>();
    private final List<Consumer<LoginState>> stateChangeListeners = new CopyOnWriteArrayList<>();
    
    private long loginStartTime = -1;
    private long lastStateChangeTime = System.currentTimeMillis();
    
    /**
     * Create a new LoginStateTracker.
     */
    public LoginStateTracker() {
        logger.debug("LoginStateTracker initialized");
        recordStateChange(LoginState.DISCONNECTED, "Tracker initialized");
    }
    
    /**
     * Set the current login state and notify listeners.
     * 
     * @param newState The new login state
     * @param details Additional details about the state change
     */
    public void setState(LoginState newState, String details) {
        if (newState == null) {
            logger.warn("Attempted to set null login state");
            return;
        }
        
        LoginState previousState = this.currentState;
        this.currentState = newState;
        
        // Track login timing
        if (newState == LoginState.CONNECTING && loginStartTime == -1) {
            loginStartTime = System.currentTimeMillis();
        } else if (newState == LoginState.LOGGED_IN && loginStartTime > 0) {
            long totalTime = System.currentTimeMillis() - loginStartTime;
            logger.info("Login completed in {} ms", totalTime);
        } else if (newState == LoginState.DISCONNECTED || newState == LoginState.FAILED) {
            loginStartTime = -1; // Reset for next login attempt
        }
        
        // Record the state change
        recordStateChange(newState, details);
        
        // Notify listeners
        notifyStateChange(newState);
        
        logger.info("Login state: {} -> {} ({})", 
            previousState, newState, details != null ? details : "no details");
    }
    
    /**
     * Set the current login state without additional details.
     */
    public void setState(LoginState newState) {
        setState(newState, null);
    }
    
    /**
     * Get the current login state.
     */
    public LoginState getCurrentState() {
        return currentState;
    }
    
    /**
     * Add a listener to be notified of state changes.
     * 
     * @param listener Consumer that will receive state change notifications
     */
    public void addStateChangeListener(Consumer<LoginState> listener) {
        if (listener != null) {
            stateChangeListeners.add(listener);
            logger.debug("Added state change listener (total: {})", stateChangeListeners.size());
        }
    }
    
    /**
     * Remove a state change listener.
     */
    public void removeStateChangeListener(Consumer<LoginState> listener) {
        if (listener != null) {
            stateChangeListeners.remove(listener);
            logger.debug("Removed state change listener (total: {})", stateChangeListeners.size());
        }
    }
    
    /**
     * Set a single state change listener (convenience method).
     */
    public void setStateChangeListener(Consumer<LoginState> listener) {
        stateChangeListeners.clear();
        if (listener != null) {
            addStateChangeListener(listener);
        }
    }
    
    /**
     * Record a state transition in the history.
     */
    private void recordStateChange(LoginState state, String details) {
        long currentTime = System.currentTimeMillis();
        long duration = currentTime - lastStateChangeTime;
        
        StateTransition transition = new StateTransition(
            state, 
            details, 
            LocalDateTime.now(),
            duration
        );
        
        stateHistory.add(transition);
        lastStateChangeTime = currentTime;
        
        // Limit history size to prevent memory issues
        if (stateHistory.size() > 100) {
            stateHistory.remove(0);
        }
    }
    
    /**
     * Notify all listeners of a state change.
     */
    private void notifyStateChange(LoginState newState) {
        for (Consumer<LoginState> listener : stateChangeListeners) {
            try {
                listener.accept(newState);
            } catch (Exception e) {
                logger.warn("Error in state change listener", e);
            }
        }
    }
    
    /**
     * Get the complete state change history.
     */
    public List<StateTransition> getStateHistory() {
        return new ArrayList<>(stateHistory);
    }
    
    /**
     * Get the most recent state transitions.
     * 
     * @param count Number of recent transitions to return
     */
    public List<StateTransition> getRecentStateHistory(int count) {
        List<StateTransition> history = getStateHistory();
        int size = history.size();
        int fromIndex = Math.max(0, size - count);
        return history.subList(fromIndex, size);
    }
    
    /**
     * Get the time spent in the current state (in milliseconds).
     */
    public long getTimeInCurrentState() {
        return System.currentTimeMillis() - lastStateChangeTime;
    }
    
    /**
     * Get the total login time if currently in progress.
     */
    public long getTotalLoginTime() {
        if (loginStartTime > 0) {
            return System.currentTimeMillis() - loginStartTime;
        }
        return -1;
    }
    
    /**
     * Check if the login is currently in progress.
     */
    public boolean isLoginInProgress() {
        return currentState.isInProgress();
    }
    
    /**
     * Check if the last login attempt failed.
     */
    public boolean hasLoginFailed() {
        return currentState.isFailed();
    }
    
    /**
     * Check if currently logged in successfully.
     */
    public boolean isLoggedIn() {
        return currentState.isConnected();
    }
    
    /**
     * Get a summary of the current login session.
     */
    public LoginSessionSummary getSessionSummary() {
        return new LoginSessionSummary(
            currentState,
            getTimeInCurrentState(),
            getTotalLoginTime(),
            stateHistory.size(),
            getRecentStateHistory(5)
        );
    }
    
    /**
     * Reset the tracker state (useful for new login attempts).
     */
    public void reset() {
        logger.info("Resetting login state tracker");
        
        currentState = LoginState.DISCONNECTED;
        loginStartTime = -1;
        lastStateChangeTime = System.currentTimeMillis();
        stateHistory.clear();
        
        recordStateChange(LoginState.DISCONNECTED, "Tracker reset");
    }
    
    /**
     * Log the current state and recent history for debugging.
     */
    public void logCurrentStatus() {
        logger.info("=== Login State Status ===");
        logger.info("Current State: {}", currentState);
        logger.info("Time in State: {} ms", getTimeInCurrentState());
        logger.info("Total Login Time: {} ms", getTotalLoginTime());
        logger.info("State Changes: {}", stateHistory.size());
        
        logger.info("Recent History:");
        List<StateTransition> recent = getRecentStateHistory(5);
        for (StateTransition transition : recent) {
            logger.info("  {} - {} ({}ms) - {}", 
                transition.getTimestamp().format(DateTimeFormatter.ofPattern("HH:mm:ss.SSS")),
                transition.getState(),
                transition.getDurationMs(),
                transition.getDetails());
        }
        logger.info("========================");
    }
    
    /**
     * Container for state transition information.
     */
    public static class StateTransition {
        private final LoginState state;
        private final String details;
        private final LocalDateTime timestamp;
        private final long durationMs;
        
        public StateTransition(LoginState state, String details, LocalDateTime timestamp, long durationMs) {
            this.state = state;
            this.details = details;
            this.timestamp = timestamp;
            this.durationMs = durationMs;
        }
        
        public LoginState getState() { return state; }
        public String getDetails() { return details; }
        public LocalDateTime getTimestamp() { return timestamp; }
        public long getDurationMs() { return durationMs; }
        
        @Override
        public String toString() {
            return String.format("StateTransition{state=%s, timestamp=%s, duration=%dms, details='%s'}", 
                state, timestamp.format(DateTimeFormatter.ofPattern("HH:mm:ss.SSS")), durationMs, details);
        }
    }
    
    /**
     * Container for login session summary information.
     */
    public static class LoginSessionSummary {
        private final LoginState currentState;
        private final long timeInCurrentState;
        private final long totalLoginTime;
        private final int totalStateChanges;
        private final List<StateTransition> recentHistory;
        
        public LoginSessionSummary(LoginState currentState, long timeInCurrentState, 
                                 long totalLoginTime, int totalStateChanges, 
                                 List<StateTransition> recentHistory) {
            this.currentState = currentState;
            this.timeInCurrentState = timeInCurrentState;
            this.totalLoginTime = totalLoginTime;
            this.totalStateChanges = totalStateChanges;
            this.recentHistory = new ArrayList<>(recentHistory);
        }
        
        public LoginState getCurrentState() { return currentState; }
        public long getTimeInCurrentState() { return timeInCurrentState; }
        public long getTotalLoginTime() { return totalLoginTime; }
        public int getTotalStateChanges() { return totalStateChanges; }
        public List<StateTransition> getRecentHistory() { return new ArrayList<>(recentHistory); }
        
        @Override
        public String toString() {
            return String.format("LoginSessionSummary{state=%s, timeInState=%dms, totalTime=%dms, changes=%d}", 
                currentState, timeInCurrentState, totalLoginTime, totalStateChanges);
        }
    }
}