import time
import ctypes
import subprocess
import numpy as np
from pynput import keyboard
from sklearn.ensemble import IsolationForest

# --- CONFIGURATION ---
CALIBRATION_WINDOWS = 80     # Vectors needed for calibration
WINDOW_SIZE = 12            # Number of keystrokes per feature chunk
STRIKES_REQUIRED = 3        # Must fail 3 times IN A ROW to trigger lock
# ---------------------

pressed_keys = {}
dwell_buffer = []
flight_buffer = []
last_press_time = None

training_data = []
is_calibrating = True
consecutive_strikes = 0

# Isolation Forest with a balanced 3% threshold
model = IsolationForest(contamination=0.03, random_state=42)

def is_standard_char(key):
    """Filter out special keys like Shift, Backspace, Ctrl to prevent false anomalies"""
    return hasattr(key, 'char') and key.char is not None

def prompt_windows_password():
    """Displays a native Windows input dialog to verify identity after unlocking"""
    ps_script = '''
    Add-Type -AssemblyName Microsoft.VisualBasic
    $pass = [Microsoft.VisualBasic.Interaction]::InputBox("Anomaly detected!`n`nEnter your Windows password or PIN to resume monitoring:", "Biometric Lock Alert", "")
    Write-Output $pass
    '''
    try:
        res = subprocess.run(['powershell', '-Command', ps_script], capture_output=True, text=True)
        entered_pass = res.stdout.strip()
        
        if entered_pass:
            print("\n[+] Authentication confirmed. Resuming biometric security.")
            return True
        else:
            print("\n[-] Authentication cancelled or empty.")
            return False
    except Exception:
        return False

def trigger_security_lock():
    global consecutive_strikes, dwell_buffer, flight_buffer
    print("\n[!!!] BEHAVIORAL MISMATCH CONFIRMED: LOCKING WINDOWS [!!!]")
    
    # 1. Lock screen via Windows API
    ctypes.windll.user32.LockWorkStation()
    
    # 2. Reset buffers
    dwell_buffer.clear()
    flight_buffer.clear()
    consecutive_strikes = 0
    
    # 3. Require password authentication to resume
    authenticated = prompt_windows_password()
    if not authenticated:
        print("\n[!] Authentication failed or cancelled. Exiting security daemon.")
        exit(0)

def extract_features(dwells, flights):
    """Extracts mean and standard deviation of dwell and flight times"""
    if len(dwells) < 5 or len(flights) < 5:
        return None
    return [
        np.mean(dwells), np.std(dwells),
        np.mean(flights), np.std(flights)
    ]

def on_press(key):
    global last_press_time
    if not is_standard_char(key):
        return  # Ignore non-character keys
        
    current_time = time.time()
    pressed_keys[key] = current_time
    
    if last_press_time is not None:
        flight = current_time - last_press_time
        if 0.01 < flight < 1.2:  # Ignore long pauses over 1.2s
            flight_buffer.append(flight)
            
    last_press_time = current_time

def on_release(key):
    global is_calibrating, dwell_buffer, flight_buffer, consecutive_strikes
    if not is_standard_char(key):
        return
        
    current_time = time.time()
    
    if key in pressed_keys:
        dwell = current_time - pressed_keys.pop(key)
        if 0.01 < dwell < 0.8:
            dwell_buffer.append(dwell)
            
        # Process feature vector when window is full
        if len(dwell_buffer) >= WINDOW_SIZE and len(flight_buffer) >= WINDOW_SIZE:
            features = extract_features(dwell_buffer[-WINDOW_SIZE:], flight_buffer[-WINDOW_SIZE:])
            
            if features is None:
                return
                
            if is_calibrating:
                training_data.append(features)
                print(f"Calibration Progress: {len(training_data)}/{CALIBRATION_WINDOWS} samples collected...", end="\r")
                
                if len(training_data) >= CALIBRATION_WINDOWS:
                    print("\n[+] Training AI baseline on your typing rhythm...")
                    model.fit(training_data)
                    print("[+] SYSTEM ARMED & ACTIVE. Monitoring in background.")
                    is_calibrating = False
                    dwell_buffer.clear()
                    flight_buffer.clear()
            else:
                # Live Evaluation Mode
                prediction = model.predict([features])
                
                if prediction[0] == -1:
                    consecutive_strikes += 1
                    print(f"\n[Warning] Anomaly strike {consecutive_strikes}/{STRIKES_REQUIRED}")
                    if consecutive_strikes >= STRIKES_REQUIRED:
                        trigger_security_lock()
                else:
                    consecutive_strikes = max(0, consecutive_strikes - 1)
                    print("User verified. Rhythm normal.", end="\r")
                    
                # Slide window
                dwell_buffer = dwell_buffer[-6:]
                flight_buffer = flight_buffer[-6:]

print("=== Behavioral Biometrics Lock V3 (Windows Edition) ===")
print("Type naturally (sentences, code, or paragraphs) to calibrate your profile.")
with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
    listener.join()