<?php

// ===========
// Users Class
// ===========

class Users extends Base {

    // Variables

    private $current_user_id = "";
    private $current_user_serial = "";
    private $current_user_name = "";
    private $current_user_email = "";
    private $pagination_size = 20;
    private $pagination_page = 1;

    // ================================
    // __construct : Class Constructor.
    // ================================

    public function __construct() {

        set_error_handler(array($this, "displayError"));

        date_default_timezone_set(self::TIMEZONE);

        $this->current_user_id = $_SESSION[self::USER_ID] ?? "";
        $this->current_user_name = $_SESSION[self::USER_NAME] ?? "";
        $this->current_user_email = $_SESSION[self::USER_EMAIL] ?? "";

        $this->pagination_page = $_SESSION[self::PAGINATION_PAGE] ?? 1;
    }

    // ===========
    // Setup Users
    // ===========

    public function setupUsers() {

        $users = $this->getUsersPage();
        $popovers = (new Popovers())->getPopovers();

        $XML = $this->mergeXML($users, "<XML/>");
        $XML = $this->mergeXML($popovers, $XML);

        $html = $this->applyXSL($XML, $this->getXSL("setupUsers"));

        $this->displayContent($html);
    }

    // ============
    // User Summary
    // ============

    public function userSummary() {

        $user_serial = filter_input(INPUT_POST, "user_serial", FILTER_SANITIZE_NUMBER_INT) ?: 0;

        $user = $this->getUser($user_serial);

        if ($user->count() == 0) {
            $this->logError("Invalid User ({$user_serial}). " . __METHOD__);
        }

        $notes = (new Notes())->getAllNotes($user_serial);
        $subscriptions = (new Subscriptions())->getAllUsersSubscriptions($user_serial);

        $XML = $this->mergeXML($user, "<XML/>");
        $XML = $this->mergeXML($subscriptions, $XML);
        $XML = $this->mergeXML($notes, $XML);

        $content = $this->applyXSL($XML, $this->getXSL("userSummary"));

        $this->displayContent($content);
    }

    // ========
    // Add User
    // ========

    public function addUser() {

        $step = filter_input(INPUT_POST, "step") ?: "prompt";

        switch ($step) {

            case "prompt":

                $popovers = (new Popovers())->getPopovers();
                $constants = $this->getConstants();

                $XML = $this->mergeXML($popovers, "<XML/>");
                $XML = $this->mergeXML($constants, $XML);

                $content = $this->applyXSL($XML, $this->getXSL("addUser"));

                $this->displayContent($content);

                break;

            case "add":

                $user_name = filter_input(INPUT_POST, "user_name") ?: "";
                $user_client_name = filter_input(INPUT_POST, "user_client_name") ?: "";
                $user_email = filter_input(INPUT_POST, "user_email") ?: "";
                $user_role = filter_input(INPUT_POST, "user_role") ?: "";

                $user_name = strtoupper(preg_replace("/\s+/", " ", $user_name));

                $user_random_password = $this->generateRandomPassword();
                $user_temp_password = password_hash($user_random_password, PASSWORD_DEFAULT);

                $user_change_password = (boolean) true;
                $user_active = (boolean) true;

                // Add the User

                $connection = $this->connect();

                $SQL = "insert into users
          
                         (  user_name,
                            user_email,
                            user_client_name,
                            user_password,
                            user_role,
                            user_change_password,
                            user_active,
                            user_creator,
                            user_created )

                  values ( :user_name,
                           :user_email,
                           :user_client_name,
                           :user_password,
                           :user_role,
                           :user_change_password,
                           :user_active,
                           :user_creator,
                           :user_created )";

                $statement = $connection->prepare($SQL);

                $statement->bindValue(":user_name", $user_name);
                $statement->bindValue(":user_email", $user_email);
                $statement->bindValue(":user_client_name", $user_client_name);
                $statement->bindValue(":user_password", $user_temp_password);
                $statement->bindValue(":user_role", $user_role);
                $statement->bindValue(":user_change_password", $user_change_password, PDO::PARAM_BOOL);
                $statement->bindValue(":user_active", $user_active, PDO::PARAM_BOOL);
                $statement->bindValue(":user_creator", $this->current_user_name);
                $statement->bindValue(":user_created", $this->getTimestamp());

                if (!$statement->execute()) {
                    trigger_error($statement->errorInfo()[2]);
                }

                $user = $this->getUserByUserName($user_name);

                // Send User Credentials

                (new Email())->emailNewUserCredentials($user, $user_random_password);

                break;

            default:

                $this->displayHome();
        }
    }

    // ===========
    // Delete User
    // ===========

    public function deleteUser() {

        $user_serial = filter_input(INPUT_POST, "user_serial", FILTER_SANITIZE_NUMBER_INT) ?: 0;

        $users = $this->getUser($user_serial);

        if ($users->count() == 0) {
            $this->logError("Invalid User ({$user_serial}): " . __METHOD__);
        }

        $SQL = "delete from users
                 where user_serial = {$user_serial}";

        $this->executeSQL($SQL);
    }

    // =========
    // Edit User
    // =========

    public function editUser() {

        $step = filter_input(INPUT_POST, "step") ?: "prompt";

        switch ($step) {

            case "prompt":

                $user_serial = (integer) filter_input(INPUT_POST, "user_serial", FILTER_SANITIZE_NUMBER_INT) ?: 0;

                $user = $this->getUser($user_serial);

                if ($user->count() === 0) {
                    $this->logError("Invalid User ({$user_serial}): " . __METHOD__);
                }

                $popovers = (new Popovers())->getPopovers();
                $constants = $this->getConstants();

                $XML = $this->mergeXML($user, "<XML/>");
                $XML = $this->mergeXML($popovers, $XML);
                $XML = $this->mergeXML($constants, $XML);

                $content = $this->applyXSL($XML, $this->getXSL("editUser"));

                $this->displayContent($content);

                break;

            case "update":

                $user_serial = filter_input(INPUT_POST, "user_serial", FILTER_SANITIZE_NUMBER_INT) ?: 0;
                $user_name = filter_input(INPUT_POST, "user_name") ?: "";
                $user_email = filter_input(INPUT_POST, "user_email") ?: "";
                $user_client_name = filter_input(INPUT_POST, "user_client_name") ?: "";
                $user_role = filter_input(INPUT_POST, "user_role") ?: "";
                $user_active = filter_input(INPUT_POST, "user_active", FILTER_VALIDATE_BOOLEAN) ?: false;

                $users = $this->getUser($user_serial);

                if ($users->count() == 0) {
                    $this->logError("Invalid User ({$user_serial}): " . __METHOD__);
                }

                // Update the User

                $SQL = "update users 
                           set user_name            = :user_name,
                               user_email           = :user_email,
                               user_client_name     = :user_client_name,
                               user_role            = :user_role,
                               user_active          = :user_active,
                               user_changer         = :user_changer,
                               user_changed         = :user_changed
                         where user_serial          = :user_serial";

                $statement = $this->connect()->prepare($SQL);

                $statement->bindValue(":user_serial", $user_serial);
                $statement->bindValue(":user_name", $user_name);
                $statement->bindValue(":user_email", $user_email);
                $statement->bindValue(":user_client_name", $user_client_name);
                $statement->bindValue(":user_role", $user_role);
                $statement->bindValue(":user_active", $user_active, PDO::PARAM_BOOL);
                $statement->bindValue(":user_changer", $this->current_user_name);
                $statement->bindValue(":user_changed", $this->getTimestamp());

                $statement->execute();

                break;

            default:

                $this->displayHome();
        }
    }

    // ====================
    // Return a User Object
    // ====================

    public function getUser($user_serial = 0) {

        $SQL = "select view_users.*
                  from view_users
                 where user_serial = {$user_serial}";

        return $this->getTable("users", $SQL);
    }

    // ====================
    // Return a User Object
    // ====================

    public function getUserByUserName($user_name) {

        $SQL = "select view_users.*
                  from view_users
                 where user_id = '{$user_name}'";

        return $this->getTable("user", $SQL);
    }

    // ======================
    // Return a User's Serial
    // ======================

    public function getUserSerial($user_id = "") {

        $users = $this->getUserByUserId($user_id);

        return ($users->count() === 0) ? 0 : (integer) $users->record->user_serial;
    }

    // =====================
    // Return a Users Object
    // =====================

    public function getUsers() {

        $SQL = "select view_users.* from view_users";

        return $this->getTable("users", $SQL);
    }

    // ==========================
    // Return a Users Page Object
    // ==========================

    public function getUsersPage() {

        // Find Criteria

        $find_user = filter_input(INPUT_POST, "find_user") ?: "";
        $find_user = $this->sanitizeString($find_user);

        $find_words = implode("|", explode(" ", $find_user));

        // Record Count

        $connection = $this->connect();

        $SQL = "select count(*) as count
                  from view_users
                 where user_name        regexp :user_name
                    or user_client_name regexp :user_client_name
                    or user_email       regexp :user_email
                    or user_role        regexp :user_role";

        $statement = $connection->prepare($SQL);

        $statement->bindValue(":user_name", $find_words);
        $statement->bindValue(":user_client_name", $find_words);
        $statement->bindValue(":user_email", $find_words);
        $statement->bindValue(":user_role", $find_words);

        $statement->execute();

        $recordset = $statement->fetch(PDO::FETCH_ASSOC);

        $count = (count($recordset) > 0) ? (integer) $recordset["count"] : (integer) 0;

        // Pagination

        $pagination = filter_input(INPUT_POST, "pagination") ?: "";

        switch ($pagination) {

            case "first" : $this->pagination_page = 1;
                break;
            case "previous" : $this->pagination_page = max(($this->pagination_page - 1), 1);
                break;
            case "next" : $this->pagination_page = ($this->pagination_page + 1);
                break;
            case "last" : $this->pagination_page = ($count / $this->pagination_size);
                break;
        }

        $offset = (($this->pagination_page * $this->pagination_size) - $this->pagination_size);
        $start = ($offset + 1);
        $end = min($count, (($start + $this->pagination_size) - 1));

        // Data

        $SQL = "select view_users.*
              from view_users
             where user_name        regexp :user_name
                or user_client_name regexp :user_client_name
                or user_email       regexp :user_email
                or user_role        regexp :user_role
             limit {$this->pagination_size}
            offset {$offset}";

        $statement = $connection->prepare($SQL);

        $statement->bindValue(":user_name", $find_words);
        $statement->bindValue(":user_client_name", $find_words);
        $statement->bindValue(":user_email", $find_words);
        $statement->bindValue(":user_role", $find_words);

        $statement->execute();

        $users = $this->getTableXML("users", $statement);

        // Insert Pagination Attributes

        $users->addAttribute("count", $count);
        $users->addAttribute("start", $start);
        $users->addAttribute("end", $end);
        $users->addAttribute("page", $this->pagination_page);

        $_SESSION[self::PAGINATION_PAGE] = $this->pagination_page;

        return $users;
    }

    // ===========================
    // Return an User Object(AJAX)
    // ===========================

    public function getUser_AJAX($user_name = "") {

        $user_name = filter_input(INPUT_POST, "user_name") ?: $user_name;

        $users = $this->getUserByUserName($user_name);

        // Create the User Object.

        $User = array();

        $User["exists"] = ($users->count() > 0) ? true : false;

        if ($_SESSION[self::AJAX]) {
            echo json_encode($User);
        } else {
            return $User;
        }
    }

    // ==========================
    // Determine if a User Exists
    // ==========================

    public function userExists() {

        $user_serial = filter_input(INPUT_POST, "user_serial", FILTER_SANITIZE_NUMBER_INT) ?: 0;
        $user_id = filter_input(INPUT_POST, "user_id") ?: "";

        $user_id = strtolower(trim(preg_replace("/\s+/", " ", $user_id)));

        $SQL = "select user_id
                  from view_users
                 where user_serial != :user_serial
                   and user_id      = :user_id";

        $statement = $this->connect()->prepare($SQL);

        $statement->bindValue(":user_serial", $user_serial);
        $statement->bindValue(":user_id", $user_id);

        $statement->execute();

        $recordset = $statement->fetchAll(PDO::FETCH_ASSOC);

        $exists = (count($recordset) > 0) ? true : false;

        echo json_encode($exists);
    }

    // =============================
    // Determine if a User is Active
    // =============================

    public function userActive() {

        $user_serial = (integer) filter_input(INPUT_POST, "user_serial", FILTER_SANITIZE_NUMBER_INT) ?: 0;

        $SQL = "select request_serial
              from requests
             where request_user = {$user_serial}";

        $requests = $this->getTable("requests", $SQL);

        $active = ($requests->count() > 0) ? true : $active;

        echo json_encode($active);
    }

    // ===============
    // Change Password
    // ===============

    public function changePassword() {

        $step = filter_input(INPUT_POST, "step") ?: "prompt";

        switch ($step) {

            case "prompt":

                $popovers = (new Popovers())->getPopovers();
                $XML = $this->mergeXML($popovers, "<XML/>");

                $content = $this->applyXSL($XML, $this->getXSL("changePassword"));
                $this->displayContent($content);

                break;

            case "change":

                $user_serial = $_SESSION[self::USER_SERIAL];
                $user_new_password = filter_input(INPUT_POST, "user_new_password") ?: "";
                $user_verify_password = filter_input(INPUT_POST, "user_verify_password") ?: "";

                if (!$user_new_password == $user_verify_password) {
                    $this->initializeSession();
                }

                $user_new_hashed_password = password_hash($user_new_password, PASSWORD_DEFAULT);

                // Update User Password

                $connection = $this->connect();

                $SQL = "update users 
                           set user_password        = :user_password,
                               user_change_password = :user_change_password,
                               user_changer         = :user_changer,
                               user_changed         = :user_changed
                         where user_serial          = :user_serial";

                $statement = $connection->prepare($SQL);

                $statement->bindValue(":user_serial", $user_serial);
                $statement->bindValue(":user_password", $user_new_hashed_password);
                $statement->bindValue(":user_change_password", 0);
                $statement->bindValue(":user_changer", $this->current_user_name);
                $statement->bindValue(":user_changed", $this->getTimestamp());

                if (!$statement->execute()) {
                    $this->debug($statement->errorInfo()[2]);
                }

                (new Journal())->journalEntry("{$_SESSION[self::USER_NAME]} changed their password.");

                break;

            default:

                $this->displayHome();
        }
    }

    // ==============
    // Reset Password
    // ==============

    public function resetPassword() {

        $reset = false;

        $user_serial = filter_input(INPUT_POST, "user_serial") ?: "";

        $user = $this->getUser($user_serial);

        $user_name = $user->record->user_name;

        $user_random_password = $this->generateRandomPassword();
        $user_temp_password = password_hash($user_random_password, PASSWORD_DEFAULT);

        // Update User Password

        $connection = $this->connect();

        $SQL = "update users 
                         set user_password        = :user_password,
                             user_change_password = :user_change_password
                       where user_serial          = :user_serial";

        $statement = $connection->prepare($SQL);

        $statement->bindValue(":user_serial", $user_serial);
        $statement->bindValue(":user_password", $user_temp_password);
        $statement->bindValue(":user_change_password", 1);

        if (!$statement->execute()) {
            $this->debug($statement->errorInfo()[2]);
        } else {
            $reset = true;
        }

        // Send User Credentials

        (new Email())->emailUserPasswordResetLink($user, $user_random_password);

        (new Journal())->journalEntry("{$_SESSION[self::USER_NAME]} reset {$user_name}'s password.");

        echo json_encode($reset);
    }

    // ===============================================
    // Update the Login timestamp for the current User
    // ===============================================

    public function updateLogin() {

        $SQL = "update users
                   set user_login  = :user_login
                 where user_serial = :user_serial";

        $statement = $this->connect()->prepare($SQL);

        $statement->bindValue(":user_login", $this->getTimestamp());
        $statement->bindValue(":user_serial", $this->current_user_serial);

        $statement->execute();
    }

    // ====================
    // Update Active Status
    // ====================

    public function updateActiveStatus() {

        $user_serial = filter_input(INPUT_POST, "user_serial", FILTER_SANITIZE_NUMBER_INT) ?: 0;
        $user_active = filter_input(INPUT_POST, "user_active", FILTER_SANITIZE_NUMBER_INT) ?: 0;

        $SQL = "update users
                   set user_active       = :user_active,
                       user_changed      = :user_changed,
                       user_changer      = :user_changer
                 where user_serial       = :user_serial";

        $statement = $this->connect()->prepare($SQL);

        $statement->bindValue(":user_serial", $user_serial);
        $statement->bindValue(":user_active", $user_active);
        $statement->bindValue(":user_changed", $this->getTimestamp());
        $statement->bindValue(":user_changer", $this->current_user_name);

        if (!$statement->execute()) {
            error_log($statement->errorInfo()[2]);
        }
    }

    // ====================================
    // Return a Users Login Activity Object
    // ====================================

    public function getLoginActivity($userserial) {

        $SQL = "select view_journal.journal_timestamp_verbose, view_journal.journal_ip
                  from view_journal
                 where view_journal.journal_origin = '{$userserial}'
                   and view_journal.journal_entry  like '{$userserial} %logged in%'";

        return $this->getTable("loginactivity", $SQL);
    }
}

?>
