<?php
require_once 'vendor/autoload.php';
require_once 'db.php';

use PragmaRX\Google2FA\Google2FA;
use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\Image\SvgImageBackEnd;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;

class TwoFactorAuth {
    private $db;
    private $google2fa;
    private const BACKUP_CODES_COUNT = 8;
    private const MAX_ATTEMPTS = 5;
    private const LOCKOUT_TIME = 900;

    public function __construct(Database $db) {
        $this->db = $db->connect();
        $this->google2fa = new Google2FA();
    }

    public function setupTwoFactor($userId, $email) {
        try {
            $secretKey = $this->google2fa->generateSecretKey();
            $backupCodes = $this->generateBackupCodes();
            
            $stmt = $this->db->prepare("SELECT id FROM two_factor_auth WHERE user_id = ?");
            $stmt->execute([$userId]);
            $exists = $stmt->fetch();
            
            if ($exists) {
                $stmt = $this->db->prepare("
                    UPDATE two_factor_auth 
                    SET secret_key = ?, backup_codes = ?, is_enabled = FALSE
                    WHERE user_id = ?
                ");
            } else {
                $stmt = $this->db->prepare("
                    INSERT INTO two_factor_auth 
                    (user_id, secret_key, backup_codes, is_enabled) 
                    VALUES (?, ?, ?, FALSE)
                ");
            }
            
            $stmt->execute([
                $exists ? $secretKey : $userId,
                json_encode($backupCodes),
                $exists ? $userId : $secretKey
            ]);

            $qrCodeUrl = $this->google2fa->getQRCodeUrl(
                'Imators Auth',
                $email,
                $secretKey
            );

            $renderer = new ImageRenderer(
                new RendererStyle(400),
                new SvgImageBackEnd()
            );
            $writer = new Writer($renderer);

            return [
                'success' => true,
                'secret' => $secretKey,
                'qrCode' => $writer->writeString($qrCodeUrl),
                'backupCodes' => $backupCodes
            ];

        } catch (PDOException $e) {
            error_log("2FA Setup Error: " . $e->getMessage());
            return ['success' => false, 'error' => 'Setup error'];
        }
    }

    public function verifyCode($userId, $code) {
        try {
            $stmt = $this->db->prepare("
                SELECT secret_key, backup_codes, attempt_count, last_attempt 
                FROM two_factor_auth 
                WHERE user_id = ? AND is_enabled = TRUE
            ");
            $stmt->execute([$userId]);
            $auth = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$auth) return false;

            if ($this->isLocked($auth['attempt_count'], $auth['last_attempt'])) {
                throw new Exception("Account temporarily locked");
            }

            $isValid = false;

            if (strlen($code) === 6) {
                $isValid = $this->google2fa->verifyKey($auth['secret_key'], $code);
            } elseif (strlen($code) === 8) {
                $backupCodes = json_decode($auth['backup_codes'], true);
                if (in_array($code, $backupCodes)) {
                    $backupCodes = array_diff($backupCodes, [$code]);
                    $this->updateBackupCodes($userId, $backupCodes);
                    $isValid = true;
                }
            }

            if ($isValid) {
                $this->resetAttempts($userId);
                return ['success' => true];
            } else {
                $this->incrementAttempts($userId);
                return ['success' => false, 'error' => 'Invalid code'];
            }

        } catch (Exception $e) {
            return ['success' => false, 'error' => $e->getMessage()];
        }
    }

        public function enableTwoFactor($userId, $code) {
    try {
        $stmt = $this->db->prepare("SELECT * FROM two_factor_auth WHERE user_id = ?");
        $stmt->execute([$userId]);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        
        error_log("Attempting to verify code: " . $code . " with secret: " . $result['secret_key']);
        
        // Important: Le window period doit être large pour la vérification initiale
        if (!$result || !$this->google2fa->verify($code, $result['secret_key'], null, 8)) {
            error_log("Verification failed for user " . $userId);
            return ['success' => false, 'error' => 'Invalid code'];
        }

        $stmt = $this->db->prepare("
            UPDATE two_factor_auth 
            SET is_enabled = TRUE, 
                updated_at = CURRENT_TIMESTAMP 
            WHERE user_id = ?
        ");
        $stmt->execute([$userId]);
        
        error_log("2FA enabled successfully for user " . $userId);
        return ['success' => true];
    } catch (Exception $e) {
        error_log("Enable 2FA Error: " . $e->getMessage());
        return ['success' => false, 'error' => $e->getMessage()];
    }
}

    private function generateBackupCodes() {
        $codes = [];
        for ($i = 0; $i < self::BACKUP_CODES_COUNT; $i++) {
            $codes[] = strtoupper(bin2hex(random_bytes(4)));
        }
        return $codes;
    }

    private function isLocked($attemptCount, $lastAttempt) {
        if ($attemptCount >= self::MAX_ATTEMPTS) {
            $lockoutEnd = strtotime($lastAttempt) + self::LOCKOUT_TIME;
            return time() < $lockoutEnd;
        }
        return false;
    }

    private function resetAttempts($userId) {
        $stmt = $this->db->prepare("
            UPDATE two_factor_auth 
            SET attempt_count = 0, last_attempt = NULL 
            WHERE user_id = ?
        ");
        $stmt->execute([$userId]);
    }

    private function incrementAttempts($userId) {
        $stmt = $this->db->prepare("
            UPDATE two_factor_auth 
            SET attempt_count = attempt_count + 1,
                last_attempt = CURRENT_TIMESTAMP 
            WHERE user_id = ?
        ");
        $stmt->execute([$userId]);
    }

    private function updateBackupCodes($userId, $codes) {
        $stmt = $this->db->prepare("
            UPDATE two_factor_auth 
            SET backup_codes = ? 
            WHERE user_id = ?
        ");
        $stmt->execute([json_encode(array_values($codes)), $userId]);
    }
}