package com.openosrs.client.login;

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

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.SecureRandom;
import java.util.Base64;

/**
 * LoginCredentials - Secure storage and management of RuneScape login credentials.
 * 
 * This class provides:
 * - Secure in-memory storage of username/password
 * - Encrypted file storage for persistent credentials
 * - Validation of credential format and requirements
 * - Safe credential handling with automatic cleanup
 * 
 * Designed for AI agents to securely manage login information.
 */
public class LoginCredentials {
    private static final Logger logger = LoggerFactory.getLogger(LoginCredentials.class);
    
    private static final String ENCRYPTION_ALGORITHM = "AES";
    private static final String CIPHER_TRANSFORMATION = "AES/ECB/PKCS5Padding";
    private static final int KEY_LENGTH = 256;
    
    private String username;
    private char[] password; // Use char array for security
    private boolean isValid = false;
    
    /**
     * Create empty credentials object.
     */
    public LoginCredentials() {
        // Empty initialization
    }
    
    /**
     * Create credentials with username and password.
     */
    public LoginCredentials(String username, String password) {
        setCredentials(username, password);
    }
    
    /**
     * Set the login credentials.
     * 
     * @param username Account username or email address
     * @param password Account password
     */
    public void setCredentials(String username, String password) {
        // Clear any existing password data
        clearPassword();
        
        this.username = validateAndTrimUsername(username);
        this.password = password != null ? password.toCharArray() : null;
        this.isValid = validateCredentials();
        
        if (isValid) {
            logger.debug("Credentials set successfully for user: {}", maskUsername(username));
        } else {
            logger.warn("Invalid credentials provided");
        }
    }
    
    /**
     * Validate and normalize the username.
     */
    private String validateAndTrimUsername(String username) {
        if (username == null || username.trim().isEmpty()) {
            return null;
        }
        
        String trimmed = username.trim().toLowerCase();
        
        // Basic validation for RuneScape username/email format
        if (trimmed.length() < 3 || trimmed.length() > 320) { // Email can be up to 320 chars
            logger.warn("Username length is invalid: {} characters", trimmed.length());
            return null;
        }
        
        // Check if it's an email or username
        if (trimmed.contains("@")) {
            // Basic email validation
            if (!isValidEmail(trimmed)) {
                logger.warn("Invalid email format");
                return null;
            }
        } else {
            // Username validation (letters, numbers, spaces, hyphens, underscores)
            if (!trimmed.matches("[a-zA-Z0-9 _-]+")) {
                logger.warn("Username contains invalid characters");
                return null;
            }
        }
        
        return trimmed;
    }
    
    /**
     * Basic email validation.
     */
    private boolean isValidEmail(String email) {
        return email.contains("@") && 
               email.contains(".") && 
               email.indexOf("@") > 0 && 
               email.lastIndexOf(".") > email.indexOf("@");
    }
    
    /**
     * Validate the current credentials.
     */
    private boolean validateCredentials() {
        if (username == null || username.trim().isEmpty()) {
            return false;
        }
        
        if (password == null || password.length == 0) {
            return false;
        }
        
        // Password should be at least 5 characters (RuneScape minimum)
        if (password.length < 5) {
            logger.warn("Password is too short (minimum 5 characters)");
            return false;
        }
        
        if (password.length > 20) {
            logger.warn("Password is too long (maximum 20 characters)");
            return false;
        }
        
        return true;
    }
    
    /**
     * Check if credentials are valid and ready to use.
     */
    public boolean isValid() {
        return isValid;
    }
    
    /**
     * Get the username (safe to access).
     */
    public String getUsername() {
        return username;
    }
    
    /**
     * Get the password as a char array.
     * WARNING: Caller is responsible for clearing the returned array.
     */
    public char[] getPassword() {
        if (password == null) {
            return null;
        }
        // Return a copy to prevent external modification
        char[] copy = new char[password.length];
        System.arraycopy(password, 0, copy, 0, password.length);
        return copy;
    }
    
    /**
     * Get password as string (use sparingly for compatibility).
     */
    public String getPasswordAsString() {
        if (password == null) {
            return null;
        }
        return new String(password);
    }
    
    /**
     * Save credentials to an encrypted file.
     * 
     * @param filePath Path where to save the encrypted credentials
     * @param masterPassword Password to encrypt the file with
     * @return true if saved successfully
     */
    public boolean saveToFile(String filePath, String masterPassword) {
        if (!isValid()) {
            logger.error("Cannot save invalid credentials");
            return false;
        }
        
        try {
            // Generate encryption key from master password
            SecretKey key = generateKeyFromPassword(masterPassword);
            
            // Create credentials data
            String credentialsData = username + "\n" + new String(password);
            
            // Encrypt the data
            Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] encryptedData = cipher.doFinal(credentialsData.getBytes(StandardCharsets.UTF_8));
            
            // Encode to Base64 for safe file storage
            String encodedData = Base64.getEncoder().encodeToString(encryptedData);
            
            // Write to file
            Path path = Paths.get(filePath);
            Files.createDirectories(path.getParent());
            Files.write(path, encodedData.getBytes(StandardCharsets.UTF_8));
            
            logger.info("Credentials saved to encrypted file: {}", filePath);
            return true;
            
        } catch (Exception e) {
            logger.error("Failed to save credentials to file", e);
            return false;
        }
    }
    
    /**
     * Load credentials from an encrypted file.
     * 
     * @param filePath Path to the encrypted credentials file
     * @param masterPassword Password to decrypt the file with
     * @return true if loaded successfully
     */
    public boolean loadFromFile(String filePath, String masterPassword) {
        try {
            Path path = Paths.get(filePath);
            if (!Files.exists(path)) {
                logger.warn("Credentials file does not exist: {}", filePath);
                return false;
            }
            
            // Read encrypted data from file
            String encodedData = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
            byte[] encryptedData = Base64.getDecoder().decode(encodedData);
            
            // Generate decryption key from master password
            SecretKey key = generateKeyFromPassword(masterPassword);
            
            // Decrypt the data
            Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] decryptedData = cipher.doFinal(encryptedData);
            
            // Parse credentials
            String credentialsData = new String(decryptedData, StandardCharsets.UTF_8);
            String[] lines = credentialsData.split("\n", 2);
            
            if (lines.length != 2) {
                logger.error("Invalid credentials file format");
                return false;
            }
            
            setCredentials(lines[0], lines[1]);
            
            logger.info("Credentials loaded from encrypted file: {}", filePath);
            return isValid();
            
        } catch (Exception e) {
            logger.error("Failed to load credentials from file", e);
            return false;
        }
    }
    
    /**
     * Overloaded method that uses a default master password.
     * WARNING: Less secure - use only for development/testing.
     */
    public boolean loadFromFile(String filePath) {
        String defaultPassword = "openosrs-agent-default-key";
        return loadFromFile(filePath, defaultPassword);
    }
    
    /**
     * Generate a secret key from a password using a simple but consistent method.
     */
    private SecretKey generateKeyFromPassword(String password) throws Exception {
        // For simplicity, we'll use a fixed salt and the password
        // In production, you'd want to use PBKDF2 with a random salt
        byte[] key = new byte[32]; // 256 bits
        byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8);
        
        // Simple key derivation (not cryptographically strong)
        for (int i = 0; i < key.length; i++) {
            key[i] = (byte) (passwordBytes[i % passwordBytes.length] ^ (i + 42));
        }
        
        return new SecretKeySpec(key, ENCRYPTION_ALGORITHM);
    }
    
    /**
     * Clear sensitive credential data from memory.
     */
    public void clear() {
        clearPassword();
        username = null;
        isValid = false;
        logger.debug("Credentials cleared from memory");
    }
    
    /**
     * Clear password from memory.
     */
    private void clearPassword() {
        if (password != null) {
            // Overwrite password data with zeros
            for (int i = 0; i < password.length; i++) {
                password[i] = '\0';
            }
            password = null;
        }
    }
    
    /**
     * Mask username for logging (show first 2 chars + asterisks).
     */
    private String maskUsername(String username) {
        if (username == null || username.length() <= 2) {
            return "***";
        }
        return username.substring(0, 2) + "*".repeat(username.length() - 2);
    }
    
    /**
     * Create a test credentials file for development.
     */
    public static boolean createTestCredentials(String filePath, String username, String password) {
        LoginCredentials creds = new LoginCredentials(username, password);
        if (!creds.isValid()) {
            return false;
        }
        return creds.saveToFile(filePath, "test-master-password");
    }
    
    @Override
    protected void finalize() throws Throwable {
        // Ensure credentials are cleared when object is garbage collected
        clear();
        super.finalize();
    }
    
    @Override
    public String toString() {
        return String.format("LoginCredentials{username='%s', valid=%s}", 
            maskUsername(username), isValid);
    }
}