<?php
/**
 * SmartThink LMS - User Class
 * 
 * Handles user authentication, registration, and profile management
 */

class User {
    private $db;
    private $table = 'users';
    
    public function __construct() {
        $this->db = Database::getInstance();
    }
    
    /**
     * Register new user
     */
    public function register($data) {
        // Validate required fields
        $required = ['username', 'email', 'password', 'first_name', 'last_name'];
        foreach ($required as $field) {
            if (empty($data[$field])) {
                throw new Exception("Field '$field' is required");
            }
        }
        
        // Check if username exists
        if ($this->getByUsername($data['username'])) {
            throw new Exception("Username already exists");
        }
        
        // Check if email exists
        if ($this->getByEmail($data['email'])) {
            throw new Exception("Email already exists");
        }
        
        // Validate email
        if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
            throw new Exception("Invalid email format");
        }
        
        // Validate password strength
        if (strlen($data['password']) < 8) {
            throw new Exception("Password must be at least 8 characters long");
        }
        
        // Hash password
        $data['password'] = hashPassword($data['password']);
        
        // Set default values
        $data['status'] = 'pending';
        $data['email_verified'] = 0;
        $data['email_verification_token'] = generateToken(32);
        $data['created_at'] = formatDate();
        
        // Detect user region
        if (empty($data['region'])) {
            $data['region'] = $this->detectRegion();
        }
        
        // Set IP and user agent
        $data['ip_address'] = getClientIP();
        $data['user_agent'] = getUserAgent();
        
        // Insert user
        $userId = $this->db->insert($this->table, $data);
        
        if (!$userId) {
            throw new Exception("Failed to register user");
        }
        
        // Create user profile
        $this->createProfile($userId, $data);
        
        // Assign default role
        $this->assignRole($userId, 'student');
        
        // Send verification email
        $this->sendVerificationEmail($userId, $data['email'], $data['email_verification_token']);
        
        return $userId;
    }
    
    /**
     * Login user
     */
    public function login($username, $password, $remember = false) {
        // Get user by username or email
        $user = $this->db->get(
            "SELECT * FROM {$this->table} WHERE (username = ? OR email = ?) AND status = 'active'",
            [$username, $username]
        );
        
        if (!$user) {
            $this->logLoginFailure(null, $username, 'user_not_found_or_inactive');
            throw new Exception("Invalid credentials");
        }
        
        // Check if account is locked
        if ($user['locked_until'] && strtotime($user['locked_until']) > time()) {
            $this->logLoginFailure($user['id'], $username, 'account_locked', [
                'locked_until' => $user['locked_until']
            ]);
            throw new Exception("Account is temporarily locked. Please try again later.");
        }
        
        // Verify password
        if (!verifyPassword($password, $user['password'])) {
            $this->incrementLoginAttempts($user['id']);
            $this->logLoginFailure($user['id'], $username, 'invalid_password', [
                'login_attempts' => isset($user['login_attempts']) ? (int)$user['login_attempts'] : null
            ]);
            throw new Exception("Invalid credentials");
        }
        
        // Reset login attempts
        $this->resetLoginAttempts($user['id']);
        
        // Update last login
        $this->updateLastLogin($user['id']);
        
        // Create session
        $this->createSession($user, $remember);
        
        // Log activity
        $this->logActivity($user['id'], 'login', 'User logged in');
        
        return $user;
    }
    
    /**
     * Logout user
     */
    public function logout() {
        if (isLoggedIn()) {
            $userId = getCurrentUserId();
            $sessionId = session_id();

            // Log activity
            $this->logActivity($userId, 'logout', 'User logged out');

            // Mark this session as inactive in user_sessions
            if ($sessionId) {
                $this->db->update('user_sessions', [
                    'is_active' => 0,
                    'ended_at' => date('Y-m-d H:i:s'),
                ], 'user_id = ? AND session_id = ?', [$userId, $sessionId]);
            }
            
            // Destroy session
            session_destroy();
            
            // Clear cookies
            if (isset($_COOKIE['remember_token'])) {
                setcookie('remember_token', '', time() - 3600, '/');
            }
        }
    }
    
    /**
     * Get user by ID
     */
    public function getById($id) {
        return $this->db->get("SELECT * FROM {$this->table} WHERE id = ?", [$id]);
    }
    
    /**
     * Get user by username
     */
    public function getByUsername($username) {
        return $this->db->get("SELECT * FROM {$this->table} WHERE username = ?", [$username]);
    }
    
    /**
     * Get user by email
     */
    public function getByEmail($email) {
        return $this->db->get("SELECT * FROM {$this->table} WHERE email = ?", [$email]);
    }
    
    /**
     * Update user profile
     */
    public function updateProfile($userId, $data) {
        // Remove sensitive fields
        unset($data['password'], $data['id'], $data['created_at']);
        
        // Update user table
        $result = $this->db->update($this->table, $data, 'id = ?', [$userId]);
        
        if ($result) {
            $this->logActivity($userId, 'profile_update', 'User profile updated');
        }
        
        return $result;
    }
    
    /**
     * Change password
     */
    public function changePassword($userId, $currentPassword, $newPassword) {
        $user = $this->getById($userId);
        if (!$user) {
            throw new Exception("User not found");
        }
        
        // Verify current password
        if (!verifyPassword($currentPassword, $user['password'])) {
            throw new Exception("Current password is incorrect");
        }
        
        // Validate new password
        if (strlen($newPassword) < 8) {
            throw new Exception("New password must be at least 8 characters long");
        }
        
        // Hash new password
        $hashedPassword = hashPassword($newPassword);
        
        // Update password
        $result = $this->db->update($this->table, ['password' => $hashedPassword], 'id = ?', [$userId]);
        
        if ($result) {
            $this->logActivity($userId, 'password_change', 'Password changed');
        }
        
        return $result;
    }
    
    /**
     * Reset password: generate token and send notifications
     */
    public function resetPassword($email) {
        $user = $this->getByEmail($email);
        if (!$user) {
            throw new Exception("Email not found");
        }
        
        $token = generateToken(32);
        $expires = date('Y-m-d H:i:s', strtotime('+1 hour'));
        
        $this->db->update($this->table, [
            'password_reset_token' => $token,
            'password_reset_expires' => $expires
        ], 'id = ?', [$user['id']]);
        
        $this->sendPasswordResetEmail($user, $token);
        $this->logActivity($user['id'], 'password_reset_requested', 'Password reset requested');
        
        return true;
    }
    
    /**
     * Verify email
     */
    public function verifyEmail($token) {
        $user = $this->db->get(
            "SELECT * FROM {$this->table} WHERE email_verification_token = ?",
            [$token]
        );
        
        if (!$user) {
            throw new Exception("Invalid verification token");
        }
        
        // Update user
        $this->db->update($this->table, [
            'email_verified' => 1,
            'email_verification_token' => null,
            'status' => 'active'
        ], 'id = ?', [$user['id']]);
        
        $this->logActivity($user['id'], 'email_verified', 'Email verified');
        
        return $user;
    }
    
    /**
     * Create user profile
     */
    private function createProfile($userId, $data) {
        // The user_profiles table stores extended profile fields; at registration
        // time we only need to create an empty profile row linked to the user.
        // First and last name are already stored on the users table.
        $profileData = [
            'user_id' => $userId,
        ];

        $this->db->insert('user_profiles', $profileData);
    }
    
    /**
     * Assign role to user
     */
    private function assignRole($userId, $role) {
        $roleData = [
            'user_id' => $userId,
            'role' => $role,
            'assigned_at' => formatDate()
        ];
        
        $this->db->insert('user_roles', $roleData);
    }
    
    /**
     * Create session
     */
    private function createSession($user, $remember = false) {
        $_SESSION['user_id'] = $user['id'];
        $_SESSION['username'] = $user['username'];
        $_SESSION['email'] = $user['email'];
        $roleData = $this->getUserRoleData($user['id']);
        $_SESSION['role'] = $roleData['role'];
        $_SESSION['permissions'] = $roleData['permissions'];
        $_SESSION['region'] = $user['region'];
        $_SESSION['login_time'] = time();

        // Register this login session for auditing and device limits
        $this->registerSession($user['id'], $roleData['role']);
        
        if ($remember) {
            $token = generateToken(64);
            $expires = time() + (30 * 24 * 60 * 60); // 30 days
            
            setcookie('remember_token', $token, $expires, '/', '', false, true);
            
            // Store token in database
            $this->db->insert('remember_tokens', [
                'user_id' => $user['id'],
                'token' => hash('sha256', $token),
                'expires_at' => date('Y-m-d H:i:s', $expires)
            ]);
        }
    }
    
    private function registerSession($userId, $role) {
        if (session_status() === PHP_SESSION_NONE) {
            if (!headers_sent()) {
                session_start();
            }
        }

        $sessionId = session_id();
        if (!$sessionId) {
            return;
        }

        $now = date('Y-m-d H:i:s');

        // For students, enforce a single active session by marking other
        // sessions inactive before inserting the new one.
        if ($role === 'student') {
            $this->db->update('user_sessions', [
                'is_active' => 0,
                'ended_at' => $now,
            ], 'user_id = ? AND role = ? AND session_id != ?', [$userId, $role, $sessionId]);
        }

        $this->db->insert('user_sessions', [
            'user_id' => $userId,
            'session_id' => $sessionId,
            'role' => $role,
            'ip_address' => getClientIP(),
            'user_agent' => getUserAgent(),
            'is_active' => 1,
            'created_at' => $now,
            'last_seen' => $now,
        ]);
    }
    
    /**
     * Get user role
     */
    private function getUserRoleData($userId) {
        $roleData = $this->db->get(
            "SELECT role, permissions FROM user_roles WHERE user_id = ? AND is_active = 1",
            [$userId]
        );

        return [
            'role' => $roleData ? ($roleData['role'] ?? 'student') : 'student',
            'permissions' => $roleData ? ($roleData['permissions'] ?? null) : null
        ];
    }
    
    /**
     * Increment login attempts
     */
    private function incrementLoginAttempts($userId) {
        $this->db->query(
            "UPDATE {$this->table} SET login_attempts = login_attempts + 1 WHERE id = ?",
            [$userId]
        );
        
        $user = $this->getById($userId);
        
        // Lock account after 5 failed attempts
        if ($user['login_attempts'] >= 5) {
            $lockUntil = date('Y-m-d H:i:s', strtotime('+30 minutes'));
            $this->db->update($this->table, ['locked_until' => $lockUntil], 'id = ?', [$userId]);
        }
    }
    
    /**
     * Reset login attempts
     */
    private function resetLoginAttempts($userId) {
        $this->db->update($this->table, [
            'login_attempts' => 0,
            'locked_until' => null
        ], 'id = ?', [$userId]);
    }
    
    /**
     * Update last login
     */
    private function updateLastLogin($userId) {
        $this->db->update($this->table, [
            'last_login' => formatDate()
        ], 'id = ?', [$userId]);
    }
    
    /**
     * Detect user region
     */
    private function detectRegion() {
        if (FEATURE_GEOLOCATION) {
            // Use IP-based geolocation
            $ip = getClientIP();
            
            // Simple region mapping based on IP ranges
            // In production, use a proper geolocation service
            $regionMapping = [
                'US' => 'usa',
                'CA' => 'usa',
                'MX' => 'usa',
                'GB' => 'europe',
                'DE' => 'europe',
                'FR' => 'europe',
                'IT' => 'europe',
                'ES' => 'europe',
                'NG' => 'africa',
                'ZA' => 'africa',
                'KE' => 'africa',
                'GH' => 'africa',
                'EG' => 'africa'
            ];
            
            // Get country from IP (simplified)
            $country = $this->getCountryFromIP($ip);
            return $regionMapping[$country] ?? DEFAULT_REGION;
        }
        
        return DEFAULT_REGION;
    }
    
    /**
     * Get country from IP (simplified implementation)
     */
    private function getCountryFromIP($ip) {
        // In production, use a proper geolocation API
        // This is a simplified version
        if (strpos($ip, '192.168.') === 0 || strpos($ip, '127.') === 0) {
            return 'US'; // Default for local development
        }
        
        // Use ipapi.co or similar service
        $response = file_get_contents("https://ipapi.co/{$ip}/country_code/");
        return $response ?: 'US';
    }
    
    /**
     * Send verification email
     */
    private function sendVerificationEmail($userId, $email, $token) {
        $verificationUrl = APP_URL . "/verify-email.php?token=" . $token;
        
        $subject = "Verify your email address";
        $message = "
            <h2>Welcome to SmartThink LMS!</h2>
            <p>Thank you for registering. Please click the link below to verify your email address:</p>
            <p><a href='{$verificationUrl}'>Verify Email Address</a></p>
            <p>If you didn't create an account, please ignore this email.</p>
        ";
        
        // Use email helper function
        sendEmail($email, $subject, $message);
    }
    
    /**
     * Send password reset notifications (email and SMS)
     */
    private function sendPasswordResetEmail(array $user, $token) {
        $resetUrl = APP_URL . "/reset-password.php?token=" . urlencode($token);
        
        $subject = "Reset your password";
        $message = "
            <h2>Password Reset Request</h2>
            <p>Click the link below to reset your password:</p>
            <p><a href='{$resetUrl}'>Reset Password</a></p>
            <p>This link will expire in 1 hour.</p>
            <p>If you didn't request this, please ignore this email.</p>
        ";
        
        if (!empty($user['email'])) {
            sendEmail($user['email'], $subject, $message);
        }
        
        if (!empty($user['phone']) && function_exists('normalizeInternationalPhone') && function_exists('mnotifySendQuickSms')) {
            $normalized = normalizeInternationalPhone(
                $user['phone'],
                $user['country'] ?? null,
                $user['region'] ?? null
            );
            
            if ($normalized) {
                $smsMessage = 'SmartThink: A password reset was requested for your account. If this was you, please check your email (' . ($user['email'] ?? '') . ') for the reset link. If not, you can ignore this message.';
                
                mnotifySendQuickSms([
                    $normalized,
                ], $smsMessage, null, [], [
                    'user_id' => (int)($user['id'] ?? 0),
                    'type' => 'password_reset',
                ]);
            }
        }
    }

    public function resetPasswordWithToken($token, $newPassword) {
        $token = trim((string)$token);
        if ($token === '') {
            throw new Exception("Invalid or expired reset link");
        }
        
        if (strlen((string)$newPassword) < 8) {
            throw new Exception("New password must be at least 8 characters long");
        }
        
        $now = date('Y-m-d H:i:s');
        $user = $this->db->get(
            "SELECT * FROM {$this->table} WHERE password_reset_token = ? AND password_reset_expires IS NOT NULL AND password_reset_expires >= ?",
            [$token, $now]
        );
        
        if (!$user) {
            throw new Exception("Invalid or expired reset link");
        }
        
        $hashed = hashPassword($newPassword);
        $this->db->update($this->table, [
            'password' => $hashed,
            'password_reset_token' => null,
            'password_reset_expires' => null,
            'login_attempts' => 0,
            'locked_until' => null,
        ], 'id = ?', [$user['id']]);
        
        $this->logActivity($user['id'], 'password_reset_completed', 'Password reset via forgot password link');
        
        return true;
    }
    
    /**
     * Log user activity
     */
    private function logActivity($userId, $action, $description) {
        $this->db->insert('activity_logs', [
            'user_id' => $userId,
            'action' => $action,
            'description' => $description,
            'ip_address' => getClientIP(),
            'user_agent' => getUserAgent(),
            'region' => $_SESSION['region'] ?? DEFAULT_REGION
        ]);
    }
    
    private function logLoginFailure($userId, $identifier, $reason, $meta = []) {
        $parts = [];
        $parts[] = 'reason=' . $reason;
        $parts[] = 'identifier=' . $identifier;
        if (!empty($meta)) {
            $parts[] = 'meta=' . json_encode($meta);
        }

        $description = 'Login failed: ' . implode(' | ', $parts);

        $region = null;
        if (isset($meta['region']) && $meta['region'] !== '') {
            $region = $meta['region'];
        }

        $this->db->insert('activity_logs', [
            'user_id' => $userId ?: null,
            'action' => 'login_failed',
            'description' => $description,
            'resource_type' => 'auth',
            'resource_id' => null,
            'ip_address' => getClientIP(),
            'user_agent' => getUserAgent(),
            'region' => $region
        ]);
    }
    
    /**
     * Get user statistics
     */
    public function getUserStats($userId) {
        $stats = [];
        
        // Enrollment stats
        $enrollments = $this->db->get(
            "SELECT COUNT(*) as total, 
                    SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed,
                    AVG(progress_percentage) as avg_progress
             FROM course_enrollments 
             WHERE student_id = ?",
            [$userId]
        );
        
        $stats['enrollments'] = $enrollments;
        
        // Certificate stats
        $certificates = $this->db->get(
            "SELECT COUNT(*) as total FROM certificates WHERE student_id = ? AND is_revoked = 0",
            [$userId]
        );
        
        $stats['certificates'] = $certificates['total'];
        
        // Activity stats
        $activity = $this->db->get(
            "SELECT COUNT(*) as total FROM activity_logs WHERE user_id = ? AND created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)",
            [$userId]
        );
        
        $stats['recent_activity'] = $activity['total'];
        
        return $stats;
    }
    
    /**
     * Search users
     */
    public function searchUsers($query, $limit = 20, $offset = 0) {
        $sql = "SELECT u.id, u.username, u.email, u.first_name, u.last_name, u.status, u.region, u.created_at,
                       ur.role
                FROM {$this->table} u
                LEFT JOIN user_roles ur ON u.id = ur.user_id
                WHERE (u.username LIKE ? OR u.email LIKE ? OR u.first_name LIKE ? OR u.last_name LIKE ?)
                ORDER BY u.created_at DESC
                LIMIT ? OFFSET ?";
        
        $searchTerm = "%{$query}%";
        return $this->db->getAll($sql, [$searchTerm, $searchTerm, $searchTerm, $searchTerm, $limit, $offset]);
    }
    
    /**
     * Get users by region
     */
    public function getUsersByRegion($region, $status = 'active') {
        return $this->db->getAll(
            "SELECT u.*, ur.role
             FROM {$this->table} u
             LEFT JOIN user_roles ur ON u.id = ur.user_id
             WHERE u.region = ? AND u.status = ?
             ORDER BY u.created_at DESC",
            [$region, $status]
        );
    }
    
    /**
     * Update user status
     */
    public function updateStatus($userId, $status) {
        $validStatuses = ['active', 'inactive', 'suspended', 'pending'];
        if (!in_array($status, $validStatuses)) {
            throw new Exception("Invalid status");
        }
        
        $result = $this->db->update($this->table, ['status' => $status], 'id = ?', [$userId]);
        
        if ($result) {
            $this->logActivity($userId, 'status_change', "User status changed to {$status}");
        }
        
        return $result;
    }
}
?>
