<?php
// verify-2fa.php - Simplified 2FA Verification API

// ============================================
// INITIALIZATION
// ============================================

// Load CORS configuration FIRST
require_once __DIR__ . '/includes/cors.php';
configureCors();

// Load environment variables
require_once __DIR__ . '../../backend-main/includes/config_loader.php';

// Load dependencies
require_once __DIR__ . '../../backend-main/includes/config.php';
require_once __DIR__ . '/includes/auth_functions.php';
require_once __DIR__ . '/includes/sms_helper.php';
require_once __DIR__ . '/includes/email_helper.php';
require_once __DIR__ . '../../backend-main/auth/AuditLogger.php';

// Set response headers
header('Content-Type: application/json');
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');

// Add security headers
header('X-Frame-Options: DENY');
header('X-Content-Type-Options: nosniff');
header('X-XSS-Protection: 1; mode=block');
header('Referrer-Policy: strict-origin-when-cross-origin');

// Handle preflight OPTIONS request
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    header('Access-Control-Max-Age: 86400');
    header('Content-Length: 0');
    http_response_code(200);
    exit();
}

// Start session
if (session_status() === PHP_SESSION_NONE) {
    ini_set('session.cookie_httponly', 1);
    ini_set('session.use_strict_mode', 1);
    ini_set('session.cookie_samesite', 'Lax');
    ini_set('session.use_only_cookies', 1);
    
    session_name('MEMBER_SESSION');
    session_start();
}

// Add request deduplication
$requestId = uniqid('req_', true);
error_log("[$requestId] === 2FA VERIFICATION START ===");

// Initialize Audit Logger
try {
    $auditLogger = new AuditLogger($pdo, null, 'LoginSystem');
} catch (Exception $e) {
    error_log("[$requestId] Audit Logger initialization failed: " . $e->getMessage());
    $auditLogger = null;
}

// ============================================
// HELPER FUNCTIONS
// ============================================

function sendResponse($data, $statusCode = 200) {
    http_response_code($statusCode);
    echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
    exit();
}

function getJsonInput() {
    $input = json_decode(file_get_contents('php://input'), true);
    if (json_last_error() !== JSON_ERROR_NONE) {
        return null;
    }
    return $input;
}

function getCsrfToken() {
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
        $_SESSION['csrf_token_time'] = time();
    }
    return $_SESSION['csrf_token'];
}

function validateCsrfToken($token) {
    if (empty($token) || empty($_SESSION['csrf_token'])) {
        return false;
    }
    
    if (!hash_equals($_SESSION['csrf_token'], $token)) {
        return false;
    }
    
    $tokenAge = time() - ($_SESSION['csrf_token_time'] ?? 0);
    if ($tokenAge > 86400) {
        unset($_SESSION['csrf_token']);
        return false;
    }
    
    return true;
}

function isOTPExpired($sentTime) {
    return (time() > ($sentTime + 300)); // 5 minutes expiry
}

function isIn2FAProcess() {
    return !empty($_SESSION['2fa_member_id']) && 
           !empty($_SESSION['2fa_sent_time']) &&
           !($_SESSION['2fa_verified'] ?? false);
}

function completeLoginAfter2FA($memberId, $requestId) {
    global $pdo, $auditLogger;
    
    // Get member data
    $stmt = $pdo->prepare("
        SELECT a.member_id, a.username, a.email, 
               m.first_name, m.last_name, m.phone_number, m.id_number,
               m.kyc_verified, m.communication_preference
        FROM member_portal_accounts a
        JOIN members m ON m.id = a.member_id
        WHERE a.member_id = ?
    ");
    $stmt->execute([$memberId]);
    $account = $stmt->fetch(PDO::FETCH_ASSOC);
    
    if (!$account) {
        throw new Exception('Member not found');
    }
    
    // Complete session setup
    session_regenerate_id(true);
    
    $_SESSION = [
        'member_id'         => $account['member_id'],
        'username'          => $account['username'],
        'email'             => $account['email'],
        'first_name'        => $account['first_name'],
        'last_name'         => $account['last_name'],
        'phone'             => $account['phone_number'],
        'id_number'         => $account['id_number'],
        'authenticated'     => true,
        'auth_type'         => $_SESSION['auth_type'] ?? 'session',
        'login_time'        => time(),
        'last_activity'     => time(),
        'ip_address'        => $_SERVER['REMOTE_ADDR'],
        'user_agent_hash'  => hash('sha256', $_SERVER['HTTP_USER_AGENT'] ?? ''),
        'session_id'       => session_id(),
        'kyc_verified'     => $account['kyc_verified'],
        'preference'       => $account['communication_preference'] ?? 'email'
    ];
    
    // Update last login
    $pdo->prepare("UPDATE member_portal_accounts SET last_login = NOW() WHERE member_id = ?")
        ->execute([$memberId]);
    
    // Clear 2FA session data
    unset($_SESSION['2fa_member_id']);
    unset($_SESSION['2fa_verified']);
    unset($_SESSION['2fa_sent_time']);
    unset($_SESSION['2fa_method']);
    unset($_SESSION['otp_attempts']);
    unset($_SESSION['otp_resend_count']);
    unset($_SESSION['last_otp_resend']);
    
    // Audit log
    if ($auditLogger) {
        $auditLogger->log($memberId, '2fa_successful', [
            'ip' => $_SERVER['REMOTE_ADDR'],
            'request_id' => $requestId
        ]);
    }
    
    return [
        'member_id' => $account['member_id'],
        'first_name' => $account['first_name'],
        'last_name' => $account['last_name'],
        'username' => $account['username'],
        'email' => $account['email']
    ];
}

// ============================================
// API ENDPOINTS
// ============================================

// GET: Return OTP status and member info
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
    try {
        if (!isIn2FAProcess()) {
            sendResponse([
                'success' => false,
                'message' => 'Invalid request. Please login first.',
                'request_id' => $requestId
            ], 400);
        }
        
        // Check OTP expiry
        if (isOTPExpired($_SESSION['2fa_sent_time'])) {
            sendResponse([
                'success' => false,
                'message' => 'OTP expired. Request new one.',
                'data' => ['expired' => true],
                'request_id' => $requestId
            ], 410);
        }
        
        $memberId = $_SESSION['2fa_member_id'];
        
        // Get member info
        $stmt = $pdo->prepare("
            SELECT m.first_name, m.last_name, m.communication_preference, 
                   m.phone_number, a.email
            FROM members m
            JOIN member_portal_accounts a ON a.member_id = m.id
            WHERE m.id = ?
        ");
        $stmt->execute([$memberId]);
        $member = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if (!$member) {
            sendResponse([
                'success' => false,
                'message' => 'Member not found',
                'request_id' => $requestId
            ], 404);
        }
        
        // Calculate remaining time
        $sentTime = $_SESSION['2fa_sent_time'];
        $remainingSeconds = ($sentTime + 300) - time();
        $remainingMinutes = ceil($remainingSeconds / 60);
        
        // Mask contact info
        $maskedContact = '';
        $method = $member['communication_preference'] ?? 'email';
        
        if ($method === 'sms' && !empty($member['phone_number'])) {
            $phone = $member['phone_number'];
            $maskedContact = preg_replace('/(\+\d{3})(\d{2})(\d{3})(\d{3})/', '$1$2XXX XXX', $phone);
        } else {
            $email = $member['email'];
            if (strpos($email, '@') !== false) {
                list($local, $domain) = explode('@', $email, 2);
                $maskedLocal = substr($local, 0, 1) . '***' . (strlen($local) > 1 ? substr($local, -1) : '');
                $maskedContact = $maskedLocal . '@' . $domain;
            }
        }
        
        $csrfToken = getCsrfToken();
        
        sendResponse([
            'success' => true,
            'message' => '2FA verification required',
            'data' => [
                'member_id' => $memberId,
                'first_name' => $member['first_name'],
                'last_name' => $member['last_name'],
                'method' => $method,
                'masked_contact' => $maskedContact,
                'csrf_token' => $csrfToken,
                'expires_in' => $remainingSeconds,
                'expires_minutes' => $remainingMinutes,
                'max_attempts' => 3,
                'session_id' => session_id()
            ],
            'request_id' => $requestId
        ]);
        
    } catch (Exception $e) {
        error_log("[$requestId] GET error: " . $e->getMessage());
        sendResponse([
            'success' => false,
            'message' => 'Failed to load verification',
            'request_id' => $requestId
        ], 500);
    }
}

// POST: Verify OTP code
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    try {
        $input = getJsonInput();
        
        if (!$input) {
            sendResponse([
                'success' => false,
                'message' => 'Invalid JSON input',
                'request_id' => $requestId
            ], 400);
        }
        
        $clientIP = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
        error_log("[$requestId] OTP verification attempt from IP: {$clientIP}");
        
        if (!isIn2FAProcess()) {
            sendResponse([
                'success' => false,
                'message' => 'Invalid request. Please login first.',
                'request_id' => $requestId
            ], 400);
        }
        
        if (isOTPExpired($_SESSION['2fa_sent_time'])) {
            sendResponse([
                'success' => false,
                'message' => 'OTP expired. Request new one.',
                'data' => ['expired' => true],
                'request_id' => $requestId
            ], 410);
        }
        
        // Validate CSRF
        $csrfToken = $input['csrf_token'] ?? '';
        if (!validateCsrfToken($csrfToken)) {
            $newToken = getCsrfToken();
            
            if ($auditLogger) {
                $auditLogger->log($_SESSION['2fa_member_id'] ?? 0, 'csrf_failure', [
                    'ip' => $clientIP,
                    'request_id' => $requestId
                ]);
            }
            
            sendResponse([
                'success' => false,
                'message' => 'Invalid security token',
                'data' => ['new_csrf_token' => $newToken],
                'request_id' => $requestId
            ], 403);
        }
        
        $otpCode = trim($input['otp_code'] ?? '');
        $memberId = $_SESSION['2fa_member_id'];
        
        if (empty($otpCode)) {
            sendResponse([
                'success' => false,
                'message' => 'Enter OTP code',
                'request_id' => $requestId
            ], 400);
        }
        
        if (!preg_match('/^\d{6}$/', $otpCode)) {
            sendResponse([
                'success' => false,
                'message' => 'Invalid OTP format. 6 digits required.',
                'request_id' => $requestId
            ], 400);
        }
        
        // Check attempt count
        if (!isset($_SESSION['otp_attempts'])) {
            $_SESSION['otp_attempts'] = 0;
        }
        
        if ($_SESSION['otp_attempts'] >= 3) {
            if ($auditLogger) {
                $auditLogger->log($memberId, 'max_attempts_reached', [
                    'ip' => $clientIP,
                    'attempts' => $_SESSION['otp_attempts'],
                    'request_id' => $requestId
                ]);
            }
            
            sendResponse([
                'success' => false,
                'message' => 'Too many failed attempts. Request new OTP.',
                'data' => ['attempts' => $_SESSION['otp_attempts']],
                'request_id' => $requestId
            ], 429);
        }
        
        // Verify OTP
        $verified = verifyOTP($memberId, $otpCode, 'login');
        
        if ($verified) {
            $_SESSION['2fa_verified'] = true;
            $_SESSION['otp_attempts'] = 0;
            
            // Complete login
            $memberData = completeLoginAfter2FA($memberId, $requestId);
            
            $newCsrfToken = getCsrfToken();
            $requiresReset = $_SESSION['force_password_reset'] ?? false;
            
            sendResponse([
                'success' => true,
                'message' => 'Login successful',
                'data' => [
                    'member' => $memberData,
                    'requires_password_reset' => $requiresReset,
                    'redirect_to' => $requiresReset ? 'change_password.php' : 'dashboard.php',
                    'csrf_token' => $newCsrfToken,
                    'session_id' => session_id()
                ],
                'request_id' => $requestId
            ]);
            
        } else {
            $_SESSION['otp_attempts'] = ($_SESSION['otp_attempts'] ?? 0) + 1;
            
            if ($auditLogger) {
                $auditLogger->log($memberId, 'otp_failed', [
                    'ip' => $clientIP,
                    'attempt' => $_SESSION['otp_attempts'],
                    'request_id' => $requestId
                ]);
            }
            
            $newCsrfToken = getCsrfToken();
            
            sendResponse([
                'success' => false,
                'message' => 'Invalid OTP',
                'data' => [
                    'attempts' => $_SESSION['otp_attempts'],
                    'remaining_attempts' => 3 - $_SESSION['otp_attempts'],
                    'csrf_token' => $newCsrfToken
                ],
                'request_id' => $requestId
            ], 401);
        }
        
    } catch (Exception $e) {
        error_log("[$requestId] POST error: " . $e->getMessage());
        
        if ($auditLogger) {
            $auditLogger->log($_SESSION['2fa_member_id'] ?? 0, 'verification_error', [
                'error' => $e->getMessage(),
                'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
                'request_id' => $requestId
            ]);
        }
        
        sendResponse([
            'success' => false,
            'message' => 'Verification failed',
            'request_id' => $requestId
        ], 500);
    }
}

// PUT/PATCH: Resend OTP
if ($_SERVER['REQUEST_METHOD'] === 'PUT' || $_SERVER['REQUEST_METHOD'] === 'PATCH') {
    try {
        $input = getJsonInput();
        
        if (!isIn2FAProcess()) {
            sendResponse([
                'success' => false,
                'message' => 'Invalid request',
                'request_id' => $requestId
            ], 400);
        }
        
        $csrfToken = $input['csrf_token'] ?? '';
        if (!validateCsrfToken($csrfToken)) {
            sendResponse([
                'success' => false,
                'message' => 'Invalid security token',
                'request_id' => $requestId
            ], 403);
        }
        
        // Check cooldown (30 seconds)
        $lastResend = $_SESSION['last_otp_resend'] ?? 0;
        $currentTime = time();
        
        if (($currentTime - $lastResend) < 30) {
            $remaining = 30 - ($currentTime - $lastResend);
            sendResponse([
                'success' => false,
                'message' => 'Wait before requesting another OTP',
                'data' => ['cooldown_remaining' => $remaining],
                'request_id' => $requestId
            ], 429);
        }
        
        // Check max resends (3 per session)
        if (!isset($_SESSION['otp_resend_count'])) {
            $_SESSION['otp_resend_count'] = 0;
        }
        
        if ($_SESSION['otp_resend_count'] >= 3) {
            sendResponse([
                'success' => false,
                'message' => 'Max resends reached. Login again.',
                'request_id' => $requestId
            ], 429);
        }
        
        // Get member info
        $memberId = $_SESSION['2fa_member_id'];
        $stmt = $pdo->prepare("
            SELECT a.email, m.first_name, m.last_name, 
                   m.communication_preference, m.phone_number
            FROM member_portal_accounts a
            JOIN members m ON m.id = a.member_id
            WHERE a.member_id = ?
        ");
        $stmt->execute([$memberId]);
        $member = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if (!$member) {
            sendResponse([
                'success' => false,
                'message' => 'Member not found',
                'request_id' => $requestId
            ], 404);
        }
        
        // Resend OTP
        $sent = sendOTPByPreference($member, 'login');
        
        if ($sent) {
            $_SESSION['2fa_sent_time'] = time();
            $_SESSION['last_otp_resend'] = time();
            $_SESSION['otp_resend_count'] = ($_SESSION['otp_resend_count'] ?? 0) + 1;
            $_SESSION['otp_attempts'] = 0;
            
            $newCsrfToken = getCsrfToken();
            
            if ($auditLogger) {
                $auditLogger->log($memberId, 'otp_resent', [
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
                    'count' => $_SESSION['otp_resend_count'],
                    'request_id' => $requestId
                ]);
            }
            
            sendResponse([
                'success' => true,
                'message' => 'OTP sent',
                'data' => [
                    'member_id' => $memberId,
                    'csrf_token' => $newCsrfToken,
                    'expires_in' => 300,
                    'resend_count' => $_SESSION['otp_resend_count'],
                    'remaining_resends' => 3 - $_SESSION['otp_resend_count']
                ],
                'request_id' => $requestId
            ]);
            
        } else {
            sendResponse([
                'success' => false,
                'message' => 'Failed to send OTP',
                'request_id' => $requestId
            ], 500);
        }
        
    } catch (Exception $e) {
        error_log("[$requestId] Resend error: " . $e->getMessage());
        sendResponse([
            'success' => false,
            'message' => 'Failed to resend OTP',
            'request_id' => $requestId
        ], 500);
    }
}

// DELETE: Cancel 2FA process
if ($_SERVER['REQUEST_METHOD'] === 'DELETE') {
    try {
        $input = getJsonInput();
        
        $csrfToken = $input['csrf_token'] ?? '';
        if (!validateCsrfToken($csrfToken)) {
            sendResponse([
                'success' => false,
                'message' => 'Invalid security token',
                'request_id' => $requestId
            ], 403);
        }
        
        $memberId = $_SESSION['2fa_member_id'] ?? null;
        
        // Clear 2FA data
        unset($_SESSION['2fa_member_id']);
        unset($_SESSION['2fa_verified']);
        unset($_SESSION['2fa_sent_time']);
        unset($_SESSION['2fa_method']);
        unset($_SESSION['otp_attempts']);
        unset($_SESSION['otp_resend_count']);
        unset($_SESSION['last_otp_resend']);
        
        if ($memberId && $auditLogger) {
            $auditLogger->log($memberId, '2fa_cancelled', [
                'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
                'request_id' => $requestId
            ]);
        }
        
        sendResponse([
            'success' => true,
            'message' => '2FA cancelled',
            'request_id' => $requestId
        ]);
        
    } catch (Exception $e) {
        error_log("[$requestId] Cancel error: " . $e->getMessage());
        sendResponse([
            'success' => false,
            'message' => 'Failed to cancel',
            'request_id' => $requestId
        ], 500);
    }
}

// Invalid method
sendResponse([
    'success' => false,
    'message' => 'Method not allowed',
    'request_id' => $requestId
], 405);

error_log("[$requestId] === 2FA VERIFICATION END ===");