package com.openosrs.client.core;

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

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

/**
 * LoginScreen - High-level login screen management for agent-friendly login.
 *
 * This class handles the complete login flow state machine and provides
 * agent-friendly methods for login automation. It manages the interaction
 * between the LoginState, NetworkEngine, and user interface.
 */
public class LoginScreen {
    private static final Logger logger = LoggerFactory.getLogger(LoginScreen.class);

    private final ClientCore clientCore;
    private final LoginState loginState;
    private final EventSystem eventSystem;
    private final ReadWriteLock screenLock = new ReentrantReadWriteLock();

    // Screen rendering state (for UI if needed)
    private final AtomicBoolean screenVisible = new AtomicBoolean(false);
    private final AtomicReference<String> currentError = new AtomicReference<>("");
    private final AtomicBoolean waitingForResponse = new AtomicBoolean(false);

    // Agent automation state
    private final AtomicReference<LoginCallback> pendingCallback = new AtomicReference<>();
    private final AtomicBoolean autoRetryEnabled = new AtomicBoolean(false);
    private final AtomicReference<LoginCredentials> retryCredentials = new AtomicReference<>();

    public LoginScreen(ClientCore clientCore) {
        this.clientCore = clientCore;
        this.loginState = clientCore.getLoginState();
        this.eventSystem = clientCore.getEventSystem();

        // Register for login events
        setupEventListeners();
    }

    public void initialize() {
        logger.debug("Initializing LoginScreen");
        screenVisible.set(true);
    }

    public void shutdown() {
        logger.debug("Shutting down LoginScreen");
        screenVisible.set(false);
        pendingCallback.set(null);
        retryCredentials.set(null);
    }

    /**
     * Set up event listeners for login state changes.
     */
    private void setupEventListeners() {
        // Listen for login success
        eventSystem.addListener(EventSystem.EventType.LOGIN_SUCCESS, event -> {
            if (event instanceof LoginState.LoginEvent) {
                onLoginSuccess((LoginState.LoginEvent) event);
            }
        });

        // Listen for login failure
        eventSystem.addListener(EventSystem.EventType.LOGIN_FAILED, event -> {
            if (event instanceof LoginState.LoginEvent) {
                onLoginFailure((LoginState.LoginEvent) event);
            }
        });

        // Listen for login state changes
        eventSystem.addListener(EventSystem.EventType.LOGIN_STATE_CHANGED, event -> {
            if (event instanceof LoginState.LoginEvent) {
                onLoginStateChanged((LoginState.LoginEvent) event);
            }
        });
    }

    /**
     * Agent-friendly login method with callback.
     */
    public void login(String username, String password, LoginCallback callback) {
        login(username, password, null, callback);
    }

    /**
     * Agent-friendly login method with OTP and callback.
     */
    public void login(String username, String password, String otp, LoginCallback callback) {
        if (waitingForResponse.get()) {
            logger.warn("Login already in progress");
            if (callback != null) {
                callback.onLoginResult(false, "Login already in progress");
            }
            return;
        }

        screenLock.writeLock().lock();
        try {
            // Store callback for later
            pendingCallback.set(callback);
            waitingForResponse.set(true);

            // Store credentials for potential retry
            retryCredentials.set(new LoginCredentials(username, password, otp));

            // Clear any previous error
            currentError.set("");

            // Attempt login through LoginState
            boolean success = loginState.attemptLogin(username, password, otp);

            if (!success) {
                // Login attempt failed validation
                waitingForResponse.set(false);
                pendingCallback.set(null);

                String error = loginState.getResponse1();
                if (error.isEmpty()) {
                    error = "Invalid credentials";
                }
                currentError.set(error);

                if (callback != null) {
                    callback.onLoginResult(false, error);
                }
                logger.warn("Login attempt failed validation: {}", error);
                return;
            }

            logger.info("Login attempt started for user: {}", username);

            // The actual network login will be handled by NetworkEngine
            // Results will come back through event listeners

        } finally {
            screenLock.writeLock().unlock();
        }
    }

    /**
     * Simplified blocking login method for agents.
     */
    public boolean loginBlocking(String username, String password) {
        return loginBlocking(username, password, null, 30000); // 30 second timeout
    }

    /**
     * Blocking login method with timeout.
     */
    public boolean loginBlocking(String username, String password, String otp, long timeoutMs) {
        final Object monitor = new Object();
        final AtomicBoolean[] result = new AtomicBoolean[]{new AtomicBoolean(false)};
        final AtomicReference<String> errorMsg = new AtomicReference<>("");

        login(username, password, otp, new LoginCallback() {
            @Override
            public void onLoginResult(boolean success, String message) {
                synchronized (monitor) {
                    result[0].set(success);
                    errorMsg.set(message);
                    monitor.notifyAll();
                }
            }
        });

        synchronized (monitor) {
            try {
                monitor.wait(timeoutMs);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                logger.warn("Login wait interrupted");
                return false;
            }
        }

        boolean success = result[0].get();
        if (!success) {
            logger.warn("Login failed: {}", errorMsg.get());
        }

        return success;
    }

    /**
     * Quick login method for agents (uses stored credentials).
     */
    public void quickLogin(LoginCallback callback) {
        String username = loginState.getUsername();
        String password = loginState.getPassword();
        String otp = loginState.getOtp();

        if (username.isEmpty() || password.isEmpty()) {
            if (callback != null) {
                callback.onLoginResult(false, "No stored credentials");
            }
            return;
        }

        login(username, password, otp.isEmpty() ? null : otp, callback);
    }

    /**
     * Enable/disable automatic retry on login failure.
     */
    public void setAutoRetry(boolean enabled) {
        autoRetryEnabled.set(enabled);
        logger.debug("Auto-retry {}", enabled ? "enabled" : "disabled");
    }

    /**
     * Force logout and return to login screen.
     */
    public void logout() {
        screenLock.writeLock().lock();
        try {
            loginState.reset();
            screenVisible.set(true);
            waitingForResponse.set(false);
            pendingCallback.set(null);
            currentError.set("");

            clientCore.setGameState(ClientCore.GameStateConstants.DISCONNECTED);

            logger.info("Logged out, returned to login screen");
        } finally {
            screenLock.writeLock().unlock();
        }
    }

    /**
     * Handle login success event.
     */
    private void onLoginSuccess(LoginState.LoginEvent event) {
        screenLock.writeLock().lock();
        try {
            screenVisible.set(false);
            waitingForResponse.set(false);
            currentError.set("");

            LoginCallback callback = pendingCallback.getAndSet(null);
            if (callback != null) {
                callback.onLoginResult(true, "Login successful");
            }

            // Update client state
            clientCore.setGameState(ClientCore.GameStateConstants.LOGGED_IN);

            logger.info("Login successful, session: {}", event.getData());
        } finally {
            screenLock.writeLock().unlock();
        }
    }

    /**
     * Handle login failure event.
     */
    private void onLoginFailure(LoginState.LoginEvent event) {
        screenLock.writeLock().lock();
        try {
            waitingForResponse.set(false);

            String errorMessage = loginState.getResponse1();
            if (errorMessage.isEmpty()) {
                errorMessage = "Login failed";
            }
            currentError.set(errorMessage);

            LoginCallback callback = pendingCallback.getAndSet(null);

            // Check for auto-retry
            if (autoRetryEnabled.get() && shouldRetry(errorMessage)) {
                logger.info("Auto-retrying login after failure: {}", errorMessage);

                LoginCredentials creds = retryCredentials.get();
                if (creds != null) {
                    // Retry after a short delay
                    eventSystem.fireEvent(EventSystem.EventType.CUSTOM_EVENT,
                        new DelayedRetryEvent(creds, callback));
                    return;
                }
            }

            if (callback != null) {
                callback.onLoginResult(false, errorMessage);
            }

            logger.warn("Login failed: {}", errorMessage);
        } finally {
            screenLock.writeLock().unlock();
        }
    }

    /**
     * Handle login state changes.
     */
    private void onLoginStateChanged(LoginState.LoginEvent event) {
        int newState = Integer.parseInt(event.getData());

        switch (newState) {
            case LoginState.LoginScreenState.CONNECTING:
                currentError.set("");
                break;
            case LoginState.LoginScreenState.CREDENTIALS:
                screenVisible.set(true);
                break;
            case LoginState.LoginScreenState.LOGGED_IN:
                screenVisible.set(false);
                break;
        }

        logger.debug("Login screen state changed to: {}", newState);
    }

    /**
     * Determine if we should retry after a login failure.
     */
    private boolean shouldRetry(String errorMessage) {
        if (errorMessage == null) return false;

        // Don't retry on credential errors
        if (errorMessage.toLowerCase().contains("invalid") ||
            errorMessage.toLowerCase().contains("incorrect") ||
            errorMessage.toLowerCase().contains("banned")) {
            return false;
        }

        // Retry on connection/server errors
        return errorMessage.toLowerCase().contains("connection") ||
               errorMessage.toLowerCase().contains("server") ||
               errorMessage.toLowerCase().contains("timeout") ||
               errorMessage.toLowerCase().contains("try again");
    }

    // Getters for current state
    public boolean isVisible() { return screenVisible.get(); }
    public boolean isWaitingForResponse() { return waitingForResponse.get(); }
    public String getCurrentError() { return currentError.get(); }
    public boolean isAutoRetryEnabled() { return autoRetryEnabled.get(); }

    public String getDisplayUsername() { return loginState.getUsername(); }
    public String getDisplayResponse0() { return loginState.getResponse0(); }
    public String getDisplayResponse1() { return loginState.getResponse1(); }
    public String getDisplayResponse2() { return loginState.getResponse2(); }
    public String getDisplayResponse3() { return loginState.getResponse3(); }
    public int getLoadingPercent() { return loginState.getLoadingPercent(); }
    public String getLoadingText() { return loginState.getLoadingText(); }

    /**
     * Callback interface for login results.
     */
    public interface LoginCallback {
        void onLoginResult(boolean success, String message);
    }

    /**
     * Simple credentials holder.
     */
    private static class LoginCredentials {
        final String username;
        final String password;
        final String otp;

        LoginCredentials(String username, String password, String otp) {
            this.username = username;
            this.password = password;
            this.otp = otp;
        }
    }

    /**
     * Event for delayed retry.
     */
    private static class DelayedRetryEvent extends EventSystem.GameEvent {
        final LoginCredentials credentials;
        final LoginCallback callback;

        DelayedRetryEvent(LoginCredentials credentials, LoginCallback callback) {
            super(EventSystem.EventType.CUSTOM_EVENT);
            this.credentials = credentials;
            this.callback = callback;
        }
    }
}
