<?php
/**
 * Login Attempts Tracker
 * Prevents brute force attacks by tracking failed login attempts
 */

class LoginAttempts {
    private $attemptsDir;
    private $maxAttempts = 5;
    private $lockoutTime = 900; // 15 minutes
    private $attemptWindow = 3600; // 1 hour
    
    public function __construct($attemptsDir = null) {
        $this->attemptsDir = $attemptsDir ?: __DIR__ . '/../tmp/login_attempts/';
        $this->ensureDirectory();
    }
    
    /**
     * Ensure attempts directory exists
     */
    private function ensureDirectory() {
        if (!is_dir($this->attemptsDir)) {
            mkdir($this->attemptsDir, 0755, true);
        }
    }
    
    /**
     * Get cache key for IP or username
     * @param string $identifier
     * @return string
     */
    private function getKey($identifier) {
        return md5($identifier . $_SERVER['REMOTE_ADDR']);
    }
    
    /**
     * Get attempts file path
     * @param string $identifier
     * @return string
     */
    private function getAttemptsFile($identifier) {
        return $this->attemptsDir . $this->getKey($identifier) . '.json';
    }
    
    /**
     * Record failed login attempt
     * @param string $identifier Username or email
     */
    public function recordFailed($identifier) {
        $file = $this->getAttemptsFile($identifier);
        $data = $this->getAttemptsData($identifier);
        
        $data['attempts'][] = time();
        
        // Keep only recent attempts within window
        $windowStart = time() - $this->attemptWindow;
        $data['attempts'] = array_filter($data['attempts'], function($timestamp) use ($windowStart) {
            return $timestamp > $windowStart;
        });
        
        $data['last_attempt'] = time();
        $data['ip'] = $_SERVER['REMOTE_ADDR'];
        
        file_put_contents($file, json_encode($data));
    }
    
    /**
     * Get attempts data
     * @param string $identifier
     * @return array
     */
    public function getAttemptsData($identifier) {
        $file = $this->getAttemptsFile($identifier);
        
        if (file_exists($file)) {
            $data = json_decode(file_get_contents($file), true);
            if ($data) {
                return $data;
            }
        }
        
        return [
            'attempts' => [],
            'last_attempt' => null,
            'ip' => null
        ];
    }
    
    /**
     * Check if login is locked
     * @param string $identifier
     * @return bool
     */
    public function isLocked($identifier) {
        $data = $this->getAttemptsData($identifier);
        
        if (empty($data['attempts'])) {
            return false;
        }
        
        $recentAttempts = array_filter($data['attempts'], function($timestamp) {
            return $timestamp > (time() - $this->lockoutTime);
        });
        
        if (count($recentAttempts) >= $this->maxAttempts) {
            return true;
        }
        
        return false;
    }
    
    /**
     * Get remaining lockout time
     * @param string $identifier
     * @return int Seconds remaining
     */
    public function getLockoutTimeRemaining($identifier) {
        $data = $this->getAttemptsData($identifier);
        
        if (empty($data['attempts'])) {
            return 0;
        }
        
        $lastAttempt = max($data['attempts']);
        $lockoutEnd = $lastAttempt + $this->lockoutTime;
        $remaining = $lockoutEnd - time();
        
        return max(0, $remaining);
    }
    
    /**
     * Clear attempts for identifier
     * @param string $identifier
     * @return bool
     */
    public function clearAttempts($identifier) {
        $file = $this->getAttemptsFile($identifier);
        if (file_exists($file)) {
            return unlink($file);
        }
        return true;
    }
    
    /**
     * Get remaining attempts before lockout
     * @param string $identifier
     * @return int
     */
    public function getRemainingAttempts($identifier) {
        $data = $this->getAttemptsData($identifier);
        
        $recentAttempts = array_filter($data['attempts'], function($timestamp) {
            return $timestamp > (time() - $this->lockoutTime);
        });
        
        return max(0, $this->maxAttempts - count($recentAttempts));
    }
    
    /**
     * Get attempts statistics
     * @return array
     */
    public function getStats() {
        $files = glob($this->attemptsDir . '*.json');
        $totalAttempts = 0;
        $lockedCount = 0;
        
        foreach ($files as $file) {
            $data = json_decode(file_get_contents($file), true);
            if ($data) {
                $totalAttempts += count($data['attempts']);
                if ($this->isLocked(basename($file, '.json'))) {
                    $lockedCount++;
                }
            }
        }
        
        return [
            'tracked_identifiers' => count($files),
            'total_failed_attempts' => $totalAttempts,
            'locked_identifiers' => $lockedCount,
            'max_attempts' => $this->maxAttempts,
            'lockout_time' => $this->lockoutTime . ' seconds'
        ];
    }
    
    /**
     * Clean old attempts records
     * @param int $maxAge Maximum age in seconds
     * @return int Number of files deleted
     */
    public function clean($maxAge = 604800) { // 7 days default
        $count = 0;
        $files = glob($this->attemptsDir . '*.json');
        $now = time();
        
        foreach ($files as $file) {
            if ($now - filemtime($file) > $maxAge) {
                if (unlink($file)) {
                    $count++;
                }
            }
        }
        
        return $count;
    }
}

// Global login attempts tracker
$loginAttempts = null;

function getLoginAttempts() {
    global $loginAttempts;
    if ($loginAttempts === null) {
        $loginAttempts = new LoginAttempts();
    }
    return $loginAttempts;
}