package com.openosrs.client;

import com.openosrs.client.core.ClientCore;
import com.openosrs.client.engine.GameEngine;
import com.openosrs.client.api.AgentAPI;
import com.openosrs.client.scripting.ScriptingFramework;
import com.openosrs.client.scripting.examples.*;
import com.openosrs.client.plugins.PluginManager;
import com.openosrs.client.plugins.examples.*;
import com.openosrs.client.login.LoginManager;
import com.openosrs.client.login.LoginState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.Arrays;
import java.util.HashMap;

/**
 * ModernizedClient - The main entry point for the agent-friendly RuneScape client.
 *
 * This class orchestrates all the core components needed for an agent to play RuneScape:
 * - Automated login system for seamless game access
 * - Game engine for handling the core game loop and rendering
 * - Agent API for programmatic interaction with the game world
 * - Scripting framework for automated gameplay
 * - Plugin system for extensibility
 *
 * The client is designed with agents in mind, providing clean APIs and
 * efficient access to game state and actions.
 */
public class ModernizedClient {
    private static final Logger logger = LoggerFactory.getLogger(ModernizedClient.class);

    private final ClientCore clientCore;
    private final GameEngine gameEngine;
    private final AgentAPI agentAPI;
    private final ScriptingFramework scriptingFramework;
    private final PluginManager pluginManager;
    private final LoginManager loginManager;
    private final ExecutorService executorService;

    private volatile boolean running = false;

    public ModernizedClient() {
        logger.info("Initializing Modernized OpenOSRS Client for Agent Play");

        this.executorService = Executors.newCachedThreadPool(r -> {
            Thread t = new Thread(r, "Client-Thread");
            t.setDaemon(true);
            return t;
        });

        // Initialize core components
        this.clientCore = new ClientCore();
        this.gameEngine = new GameEngine(clientCore);
        this.agentAPI = new AgentAPI(clientCore, gameEngine);
        this.scriptingFramework = new ScriptingFramework(agentAPI, clientCore);
        this.pluginManager = new PluginManager(agentAPI, clientCore);
        this.loginManager = new LoginManager(clientCore);

        // Set up login state monitoring
        setupLoginMonitoring();

        // Register example scripts and plugins
        registerExampleScripts();
        registerExamplePlugins();

        logger.info("Client components initialized successfully");
    }

    /**
     * Start the client and begin the game loop.
     * This method initializes all subsystems and begins running the game.
     *
     * @return CompletableFuture that completes when the client is fully started
     */
    public CompletableFuture<Void> start() {
        if (running) {
            logger.warn("Client is already running");
            return CompletableFuture.completedFuture(null);
        }

        logger.info("Starting client...");
        running = true;

        return CompletableFuture.runAsync(() -> {
            try {
                // Initialize core systems
                clientCore.initialize();
                gameEngine.initialize();
                pluginManager.initialize();
                scriptingFramework.setEnabled(true);

                logger.info("All subsystems initialized");

                // Start the main game loop
                gameEngine.startGameLoop();

                logger.info("Client started successfully - ready for agent interaction");

            } catch (Exception e) {
                logger.error("Failed to start client", e);
                running = false;
                throw new RuntimeException("Client startup failed", e);
            }
        }, executorService);
    }

    /**
     * Check if the client is currently running.
     */
    public boolean isRunning() {
        return running;
    }

    /**
     * Stop the client and clean up all resources.
     */
    public void stop() {
        if (!running) {
            logger.warn("Client is not running");
            return;
        }

        logger.info("Stopping client...");
        running = false;

        try {
            // Logout from game if logged in
            if (loginManager.isLoggedIn()) {
                logger.info("Logging out from game...");
                loginManager.logout().get(10, TimeUnit.SECONDS);
            }
            
            scriptingFramework.shutdown();
            pluginManager.disableAllPlugins();
            gameEngine.shutdown();
            clientCore.shutdown();

            executorService.shutdown();

            logger.info("Client stopped successfully");
        } catch (Exception e) {
            logger.error("Error during client shutdown", e);
        }
    }

    /**
     * Get the Agent API for programmatic game interaction.
     */
    public AgentAPI getAgentAPI() {
        return agentAPI;
    }

    /**
     * Get the scripting framework for running automated scripts.
     */
    public ScriptingFramework getScriptingFramework() {
        return scriptingFramework;
    }

    /**
     * Get the login manager for automated game access.
     */
    public LoginManager getLoginManager() {
        return loginManager;
    }

    /**
     * Set login credentials for automated login.
     * 
     * @param username Account username or email
     * @param password Account password
     */
    public void setLoginCredentials(String username, String password) {
        loginManager.setCredentials(username, password);
        logger.info("Login credentials configured for automated login");
    }

    /**
     * Load login credentials from an encrypted file.
     * 
     * @param credentialsFile Path to encrypted credentials file
     * @return true if credentials loaded successfully
     */
    public boolean loadLoginCredentials(String credentialsFile) {
        boolean loaded = loginManager.loadCredentials(credentialsFile);
        if (loaded) {
            logger.info("Login credentials loaded from file: {}", credentialsFile);
        } else {
            logger.warn("Failed to load login credentials from file: {}", credentialsFile);
        }
        return loaded;
    }

    /**
     * Perform automated login to RuneScape.
     * This method will connect to servers, select a world, and authenticate.
     * 
     * @return CompletableFuture that completes when login is successful
     */
    public CompletableFuture<Boolean> login() {
        return login(60); // Default 60 second timeout
    }

    /**
     * Perform automated login with custom timeout.
     * 
     * @param timeoutSeconds Maximum time to wait for login completion
     * @return CompletableFuture that completes when login is successful
     */
    public CompletableFuture<Boolean> login(int timeoutSeconds) {
        logger.info("Starting automated login process...");
        return loginManager.login(timeoutSeconds)
            .thenApply(success -> {
                if (success) {
                    logger.info("Automated login completed successfully - agent ready for gameplay");
                } else {
                    logger.error("Automated login failed: {}", loginManager.getLastError());
                }
                return success;
            });
    }

    /**
     * Get the current login state.
     */
    public LoginState getLoginState() {
        return loginManager.getCurrentState();
    }

    /**
     * Check if currently logged into the game and ready for agent actions.
     */
    public boolean isLoggedIn() {
        return loginManager.isLoggedIn();
    }

    /**
     * Get detailed login status information.
     */
    public LoginManager.LoginStatus getLoginStatus() {
        return loginManager.getStatus();
    }

    /**
     * Enable or disable auto-reconnect on disconnection.
     */
    public void setAutoReconnect(boolean autoReconnect) {
        loginManager.setAutoReconnect(autoReconnect);
    }

    /**
     * Get the plugin manager for loading and managing plugins.
     */
    public PluginManager getPluginManager() {
        return pluginManager;
    }

    /**
     * Set up login state monitoring and callbacks.
     */
    private void setupLoginMonitoring() {
        loginManager.setStateChangeCallback(state -> {
            logger.debug("Login state changed to: {}", state);
            
            // Handle specific state changes
            switch (state) {
                case LOGGED_IN:
                    logger.info("Successfully logged in - agent ready for gameplay");
                    // Could trigger agent initialization here
                    break;
                case FAILED:
                    logger.error("Login failed: {}", loginManager.getLastError());
                    break;
                case DISCONNECTED:
                    logger.info("Disconnected from game server");
                    break;
                default:
                    // Other states are just progress updates
                    break;
            }
        });
        
        // Enable auto-reconnect by default for agents
        loginManager.setAutoReconnect(true);
        
        logger.debug("Login monitoring configured");
    }

    /**
     * Register example scripts for agent use.
     */
    private void registerExampleScripts() {
        logger.info("Registering example scripts");

        // Register woodcutting script
        scriptingFramework.registerScript("woodcutting", new WoodcuttingScript());

        // Register combat training script (goblins, eat lobster at 40 HP)
        scriptingFramework.registerScript("combat-training",
            new CombatTrainingScript(3, true, 373, 40));

        // Register banking script
        scriptingFramework.registerScript("banking",
            new BankingScript(
                Arrays.asList(1521), // Deposit oak logs
                new HashMap<Integer, Integer>() {{
                    put(1351, 1); // Withdraw 1 bronze axe
                }}
            ));

        logger.info("Example scripts registered");
    }

    /**
     * Register example plugins for agent enhancement.
     */
    private void registerExamplePlugins() {
        logger.info("Registering example plugins");

        // Register core plugins
        pluginManager.registerPlugin(new AutoHealPlugin());
        pluginManager.registerPlugin(new PerformanceMonitorPlugin());
        pluginManager.registerPlugin(new AntiIdlePlugin());
        pluginManager.registerPlugin(new ExperienceTrackerPlugin());

        // Enable essential plugins by default
        pluginManager.enablePlugin("Performance Monitor");
        pluginManager.enablePlugin("Experience Tracker");

        logger.info("Example plugins registered and enabled");
    }

    /**
     * Main entry point for the agent-friendly client.
     */
    public static void main(String[] args) {
        logger.info("=== OpenOSRS Modernized Client ===");
        logger.info("Agent-Friendly RuneScape Client Starting...");
        logger.info("Designed for AI agents to enjoy playing RuneScape");

        ModernizedClient client = new ModernizedClient();

        // Add shutdown hook for clean exit
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            logger.info("Shutdown hook triggered");
            client.stop();
        }));

        try {
            // Start the client
            client.start().get();

            // Display agent capabilities
            logger.info("=== Agent Features Available ===");
            logger.info("• Automated Login: Seamless game access");
            logger.info("• Scripting Framework: Automated gameplay");
            logger.info("• Plugin System: Enhanced capabilities");
            logger.info("• Clean API: Direct game world interaction");
            logger.info("• Event System: Real-time game state monitoring");
            logger.info("=====================================\n");
            
            // Check for credentials and demonstrate login capability
            demonstrateLoginCapability(client, args);

            // Keep the main thread alive while client runs
            while (client.isRunning()) {
                Thread.sleep(1000);
                
                // Periodically log login status for monitoring
                if (System.currentTimeMillis() % 30000 < 1000) { // Every 30 seconds
                    logLoginStatus(client);
                }
            }

        } catch (Exception e) {
            logger.error("Fatal error in client", e);
            System.exit(1);
        }
    }
    
    /**
     * Demonstrate login capability based on provided arguments.
     */
    private static void demonstrateLoginCapability(ModernizedClient client, String[] args) {
        logger.info("=== Automated Login System Demo ===");
        
        // Check for credentials file argument
        String credentialsFile = null;
        for (int i = 0; i < args.length - 1; i++) {
            if ("--credentials".equals(args[i])) {
                credentialsFile = args[i + 1];
                break;
            }
        }
        
        if (credentialsFile != null) {
            logger.info("Loading credentials from file: {}", credentialsFile);
            if (client.loadLoginCredentials(credentialsFile)) {
                logger.info("Credentials loaded successfully - starting automated login...");
                
                // Attempt automated login
                client.login(30).thenAccept(success -> {
                    if (success) {
                        logger.info("=== AUTOMATED LOGIN SUCCESSFUL ===");
                        logger.info("Agent is now ready for gameplay!");
                        logger.info("Login Status: {}", client.getLoginStatus());
                    } else {
                        logger.warn("Automated login failed: {}", client.getLoginManager().getLastError());
                    }
                }).exceptionally(throwable -> {
                    logger.error("Login process error", throwable);
                    return null;
                });
            } else {
                logger.warn("Failed to load credentials from file");
            }
        } else {
            logger.info("No credentials provided. To test automated login:");
            logger.info("  java -jar client.jar --credentials /path/to/credentials.dat");
            logger.info("\nTo create credentials file programmatically:");
            logger.info("  client.setLoginCredentials(\"username\", \"password\")");
            logger.info("  client.getLoginManager().getCredentials().saveToFile(\"/path/to/creds.dat\", \"master-password\")");
        }
        
        logger.info("====================================\n");
    }
    
    /**
     * Log current login status for monitoring.
     */
    private static void logLoginStatus(ModernizedClient client) {
        try {
            LoginManager.LoginStatus status = client.getLoginStatus();
            if (client.isLoggedIn()) {
                logger.info("Login Status: ONLINE - World: {}, Ping: {}ms", 
                    status.getCurrentWorld(), status.getPing());
            } else if (status.getState().isInProgress()) {
                logger.info("Login Status: {} - {}", status.getState(), 
                    status.getState().getDescription());
            }
        } catch (Exception e) {
            logger.debug("Error checking login status", e);
        }
    }
}
