<?php
require_once 'Database.php';

class Auth {
    private $db;

    public function __construct() {
        $this->db = new Database();
        // Start session if not already started
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }
    }

    public function isLoggedIn() {
        return isset($_SESSION['user']);
    }

    public function getUser() {
        return $this->isLoggedIn() ? $_SESSION['user'] : null;
    }

    private function validatePassword($password) {
        $errors = [];
        if (strlen($password) < 8) {
            $errors[] = "Password must be at least 8 characters long";
        }
        if (!preg_match('/[A-Z]/', $password)) {
            $errors[] = "Password must contain at least one uppercase letter";
        }
        if (!preg_match('/[a-z]/', $password)) {
            $errors[] = "Password must contain at least one lowercase letter";
        }
        if (!preg_match('/[0-9]/', $password)) {
            $errors[] = "Password must contain at least one number";
        }
        if (!preg_match('/[^A-Za-z0-9]/', $password)) {
            $errors[] = "Password must contain at least one special character";
        }
        return $errors;
    }

    private function validatePhone($phone) {
        if (empty($phone)) return true; // Phone is optional
        return preg_match('/^\+?[\d\s-]{10,}$/', $phone);
    }

    private function sanitizeName($name) {
        return htmlspecialchars(trim($name), ENT_QUOTES, 'UTF-8');
    }

    public function register($email, $password, $name, $role = 'customer', $phone = '') {
        // Validate email
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new Exception("Invalid email format");
        }

        // Check if email exists
        $checkStmt = $this->db->query("SELECT id FROM users WHERE email = ?", [$email]);
        if ($checkStmt->rowCount() > 0) {
            throw new Exception("Email already registered");
        }

        // Validate password
        $passwordErrors = $this->validatePassword($password);
        if (!empty($passwordErrors)) {
            throw new Exception(implode(". ", $passwordErrors));
        }

        // Validate phone if provided
        if (!empty($phone) && !$this->validatePhone($phone)) {
            throw new Exception("Invalid phone number format");
        }

        // Sanitize name
        $name = $this->sanitizeName($name);
        if (empty($name)) {
            throw new Exception("Name is required");
        }

        // Validate role
        $allowedRoles = ['customer', 'shop_owner', 'rider', 'admin'];
        if (!in_array($role, $allowedRoles)) {
            throw new Exception("Invalid role specified");
        }

        $hashedPassword = password_hash($password, PASSWORD_DEFAULT);
        
        $stmt = $this->db->query(
            "INSERT INTO users (email, password, name, role, phone) VALUES (?, ?, ?, ?, ?)",
            [$email, $hashedPassword, $name, $role, $phone]
        );

        return $this->db->lastInsertId();
    }

    public function login($email, $password) {
        try {
            // Validate email format
            if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
                throw new Exception("Please enter a valid email address");
            }

            // Validate password
            if (empty($password)) {
                throw new Exception("Password is required");
            }

            // Get user by email
            $stmt = $this->db->query(
                "SELECT * FROM users WHERE email = ? LIMIT 1",
                [$email]
            );

            $user = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$user) {
                // Use a generic message for security
                throw new Exception("Invalid email or password");
            }

            // Verify password
            if (!password_verify($password, $user['password'])) {
                error_log("Failed login attempt for email: " . $email);
                throw new Exception("Invalid email or password");
            }

            // Remove sensitive data
            unset($user['password']);
            
            // Regenerate session ID to prevent session fixation
            session_regenerate_id(true);
            
            // Store user data in session
            $_SESSION['user'] = $user;
            $_SESSION['last_activity'] = time();

            return $user;

        } catch (PDOException $e) {
            error_log("Database error during login: " . $e->getMessage());
            throw new Exception("An error occurred during login. Please try again.");
        } catch (Exception $e) {
            // Re-throw authentication exceptions
            throw $e;
        }
    }

    public function logout() {
        session_destroy();
        session_start();
    }

    public function requireAuth() {
        if (!$this->isLoggedIn()) {
            http_response_code(401);
            echo json_encode(['error' => 'Authentication required']);
            exit;
        }
        return $this->getUser();
    }

    public function hasRole($roles) {
        $user = $this->getUser();
        if (!$user) {
            return false;
        }

        if (!is_array($roles)) {
            $roles = [$roles];
        }

        // An admin has all permissions
        if (in_array('admin', $roles) && $user['role'] === 'admin') return true;
        if ($user['role'] === 'admin') return true;

        return in_array($user['role'], $roles);
    }

    public function requireRole($roles) {
        $user = $this->requireAuth();
        
        if (!$this->hasRole($roles)) {
            http_response_code(403);
            $required = is_array($roles) ? implode(', ', $roles) : $roles;
            echo json_encode(['error' => 'Insufficient permissions. Required role(s): ' . $required]);
            exit;
        }
        return $user;
    }

    public function updateUser($user_id, $data) {
        $fields = [];
        $params = [];

        if (isset($data['name'])) {
            $name = $this->sanitizeName($data['name']);
            if (empty($name)) throw new Exception("Name cannot be empty.");
            $fields[] = "name = ?";
            $params[] = $name;
        }

        if (isset($data['phone'])) {
            if (!empty($data['phone']) && !$this->validatePhone($data['phone'])) {
                throw new Exception("Invalid phone number format.");
            }
            $fields[] = "phone = ?";
            $params[] = $data['phone'];
        }

        if (isset($data['avatar'])) {
            $fields[] = "avatar = ?";
            $params[] = $data['avatar'];
        }

        if (isset($data['password'])) {
            $passwordErrors = $this->validatePassword($data['password']);
            if (!empty($passwordErrors)) throw new Exception(implode(". ", $passwordErrors));
            $fields[] = "password = ?";
            $params[] = password_hash($data['password'], PASSWORD_DEFAULT);
        }

        if (empty($fields)) return $this->getUser();

        $params[] = $user_id;
        $this->db->query("UPDATE users SET " . implode(', ', $fields) . " WHERE id = ?", $params);

        // Refresh session data
        return $this->loginWithId($user_id);
    }

    /**
     * Retrieves users by their role.
     * @param string $role The role to filter by (e.g., 'rider', 'shop_owner').
     * @return array An array of user objects.
     */
    public function getUsersByRole(string $role): array {
        return $this->db->query("SELECT id, email, name FROM users WHERE role = ?", [$role])->fetchAll(PDO::FETCH_ASSOC);
    }

    public function loginWithId($user_id) {
        $stmt = $this->db->query("SELECT * FROM users WHERE id = ?", [$user_id]);
        $user = $stmt->fetch(PDO::FETCH_ASSOC);
        if ($user) {
            unset($user['password']);
            $_SESSION['user'] = $user;
            return $user;
        }
        return null;
    }
}
?>