<?php
/**
 * UserModel
 * Handles all the user stuff.
 */
class UserModel
{
    public static function checkOwnership($user_id)
    {
        if (Session::userIsAdmin()) {
            return true;
        } else {
            if ($user_id == Session::get('user_id')) {
                return true;
            } else {
                Session::add('feedback_negative',
                             Text::get('FEEDBACK_PROFILE_INSUFFICIENT_PRIVILEGES'));
                Redirect::to('profiles/view_own');
                exit();
            }
        }
    }

    /**
     * Gets an array that contains all the users in the database.
     * The array's keys are the user ids.
     * Each array element is an object, containing a specific user's data.
     *
     * @return array The profiles of all users
     */
    public static function getProfilesWithDetails()
    {
        $prefix = Config::get('DB_PREFIX');
        $database = DatabaseFactory::getFactory()->getConnection();

        $sql = "SELECT U.user_id                    AS id,
                       P.id                         AS profile_id,
                       U.user_name                  AS name,
                       U.user_email                 AS email,
                       U.user_active                AS active,
                       U.user_approved              AS approved,
                       U.user_creation_timestamp    AS creation_timestamp,
                       U.user_last_login_timestamp  AS last_login_timestamp,
                       P.company_name               AS company_name
                FROM ".$prefix."users               AS U
                LEFT JOIN ".$prefix."profiles       AS P ON U.user_id = P.user_id
                ";
        $query = $database->prepare($sql);
        $query->execute();

        $users = array();

        foreach ($query->fetchAll() as $user) {
            $users[$user->id]                               = new stdClass();
            $users[$user->id]->id                           = $user->id;
            $users[$user->id]->profile_id                   = $user->profile_id;
            $users[$user->id]->user_name                    = $user->name;
            $users[$user->id]->user_email                   = $user->email;
            $users[$user->id]->user_active                  = $user->active;
            $users[$user->id]->user_approved                = $user->approved;
            $users[$user->id]->user_creation_timestamp      = $user->creation_timestamp;
            $users[$user->id]->user_last_login_timestamp    = $user->last_login_timestamp;
            $users[$user->id]->company_name                 = $user->company_name;
            $users[$user->id]->offers_number                = UserProfileModel::getNumber(1,$user->profile_id);
            $users[$user->id]->request_number               = UserProfileModel::getNumber(2,$user->profile_id);
        }

        return $users;
    }

    /**
     * Gets an object that contains all the data of a user in the database.
     *
     * @return Object The profile of a user
     */
    public static function getProfileWithDetails($user_id)
    {
        $prefix = Config::get('DB_PREFIX');
        $database = DatabaseFactory::getFactory()->getConnection();

        $sql = "SELECT U.user_id                    AS id,
                       P.id                         AS profile_id,
                       U.user_name                  AS name,
                       U.user_email                 AS email,
                       U.user_active                AS active,
                       U.user_approved              AS approved,
                       U.user_creation_timestamp    AS creation_timestamp,
                       U.user_last_login_timestamp  AS last_login_timestamp,
                       P.company_name               AS company_name,
                       P.company_claim              AS company_claim,
                       P.company_logo_path          AS company_logo_path,
                       P.admin_featured             AS admin_featured,
                       P.other_featured             AS other_featured
                FROM ".$prefix."users               AS U
                LEFT JOIN ".$prefix."profiles       AS P ON U.user_id = P.user_id
                WHERE U.user_id = :user_id
                ";
        $query = $database->prepare($sql);
        $query->execute(array(':user_id' => $user_id));

        $user = $query->fetch();

        $user->offers_number                = UserProfileModel::getNumber(1,$user->profile_id);
        $user->request_number               = UserProfileModel::getNumber(2,$user->profile_id);

        return $user;
    }

    /**
     * Gets a user's profile data, according to the given $user_id
     * @param int $user_id The user's id
     * @return mixed The selected user's profile
     */
    public static function getPublicProfileOfUser($user_id)
    {
        $prefix = Config::get('DB_PREFIX');
        $database = DatabaseFactory::getFactory()->getConnection();

        $sql = "SELECT user_id,
                       user_name,
                       user_email,
                       user_active
                FROM ".$prefix."users
                WHERE user_id = :user_id
                LIMIT 1";
        $query = $database->prepare($sql);
        $query->execute(array(':user_id' => $user_id));

        $user = $query->fetch();

        if ($query->rowCount() == 0) {
            Session::add('feedback_negative', Text::get('FEEDBACK_USER_DOES_NOT_EXIST'));
        }

        return $user;
    }

    /**
     * Gets a user's profile data, according to the given $user_id
     * @param int $user_id The user's id
     * @return mixed The selected user's profile
     */
    public static function getUser($user_id)
    {
        $prefix = Config::get('DB_PREFIX');
        $database = DatabaseFactory::getFactory()->getConnection();

        $sql = "SELECT user_id,
                       user_name,
                       user_email,
                       user_active,
                       user_creation_timestamp,
                       user_last_login_timestamp
                FROM ".$prefix."users
                WHERE user_id = :user_id
                LIMIT 1";
        $query = $database->prepare($sql);
        $query->execute(array(':user_id' => $user_id));

        $user = $query->fetch();

        if ($query->rowCount() == 0) {
            Session::add('feedback_negative', Text::get('FEEDBACK_USER_DOES_NOT_EXIST'));
        }

        return $user;
    }

    /**
     * @param $user_name_or_email
     *
     * @return mixed
     */
    public static function getUserDataByUserNameOrEmail($user_name_or_email)
    {
        $prefix = Config::get('DB_PREFIX');
        $database = DatabaseFactory::getFactory()->getConnection();

        $query = $database->prepare("SELECT user_id,
                                            user_name,
                                            user_email
                                     FROM ".$prefix."users
                                     WHERE (user_name = :user_name_or_email OR
                                            user_email = :user_name_or_email)
                                     LIMIT 1");
        $query->execute(array(':user_name_or_email' => $user_name_or_email));

        return $query->fetch();
    }

    /**
     * Checks if a username is already taken
     *
     * @param $user_name string username
     *
     * @return bool
     */
    public static function doesUsernameAlreadyExist($user_name)
    {
        $prefix = Config::get('DB_PREFIX');
        $database = DatabaseFactory::getFactory()->getConnection();

        $query = $database->prepare("SELECT user_id
                                     FROM ".$prefix."users
                                     WHERE user_name = :user_name
                                     LIMIT 1");
        $query->execute(array(':user_name' => $user_name));
        if ($query->rowCount() == 0) {
            return false;
        }
        return true;
    }

    /**
     * Checks if a email is already used
     *
     * @param $user_email string email
     *
     * @return bool
     */
    public static function doesEmailAlreadyExist($user_email)
    {
        $prefix = Config::get('DB_PREFIX');
        $database = DatabaseFactory::getFactory()->getConnection();

        $query = $database->prepare("SELECT user_id
                                     FROM ".$prefix."users
                                     WHERE user_email = :user_email
                                     LIMIT 1");
        $query->execute(array(':user_email' => $user_email));
        if ($query->rowCount() == 0) {
            return false;
        }
        return true;
    }

    public static function setUserActive($user_id)
    {
        $prefix = Config::get('DB_PREFIX');
        $database = DatabaseFactory::getFactory()->getConnection();

        $query = $database->prepare("UPDATE ".$prefix."users
                                     SET user_active = 1
                                     WHERE user_id = :user_id
                                     LIMIT 1");
        $query->execute(array(':user_id' => $user_id));
        if ($query->rowCount() == 0) {
            return false;
        }
        return true;
    }

    /**
     * Writes new username to database
     *
     * @param $user_id int user id
     * @param $new_user_name string new username
     *
     * @return bool
     */
    public static function saveNewUserName($user_id, $new_user_name)
    {
        $prefix = Config::get('DB_PREFIX');
        $database = DatabaseFactory::getFactory()->getConnection();

        $query = $database->prepare("UPDATE ".$prefix."users
                                     SET user_name = :user_name
                                     WHERE user_id = :user_id
                                     LIMIT 1");
        $query->execute(array(':user_name' => $new_user_name,
                              ':user_id' => $user_id));
        if ($query->rowCount() == 1) {
            return true;
        }
        return false;
    }

    /**
     * Writes new email address to database
     *
     * @param $user_id int user id
     * @param $new_user_email string new email address
     *
     * @return bool
     */
    public static function saveNewEmailAddress($user_id, $new_user_email)
    {
        $prefix = Config::get('DB_PREFIX');
        $database = DatabaseFactory::getFactory()->getConnection();

        $query = $database->prepare("UPDATE ".$prefix."users
                                     SET user_email = :user_email
                                     WHERE user_id = :user_id
                                     LIMIT 1");
        $query->execute(array(':user_email' => $new_user_email,
                              ':user_id' => $user_id));
        if ($query->rowCount() == 1) {
            return true;
        }
        return false;
    }

    /**
     * Edit the user's name, provided in the editing form
     *
     * @param $new_user_name string The new username
     *
     * @return bool success status
     */
    public static function editUserName($user_id, $new_user_name)
    {
        // username cannot be empty and must be azAZ09 and 2-64 characters
        if (!preg_match("/^[a-zA-Z0-9]{2,64}$/", $new_user_name))
        {
            Session::add('feedback_negative',
                         Text::get('FEEDBACK_USERNAME_DOES_NOT_FIT_PATTERN'));
            return false;
        }

        // clean the input, strip usernames longer than 64 chars (maybe fix this ?)
        $new_user_name = substr(strip_tags($new_user_name), 0, 64);

        // check if new username already exists
        if (UserModel::doesUsernameAlreadyExist($new_user_name))
        {
            Session::add('feedback_negative',
                         Text::get('FEEDBACK_USERNAME_ALREADY_TAKEN'));
            return false;
        }

        $status_of_action = UserModel::saveNewUserName($user_id,
                                                       $new_user_name);
        if ($status_of_action)
        {
            Session::add('feedback_positive',
                         Text::get('FEEDBACK_USERNAME_CHANGE_SUCCESSFUL'));
            return true;
        } else {
            Session::add('feedback_negative', Text::get('FEEDBACK_UNKNOWN_ERROR'));
            return false;
        }
    }

    /**
     * Edit the user's email
     *
     * @param $new_user_email
     *
     * @return bool success status
     */
    public static function editUserEmail($user_id, $new_user_email)
    {
        // email provided ?
        if (empty($new_user_email))
        {
            Session::add('feedback_negative', Text::get('FEEDBACK_EMAIL_FIELD_EMPTY'));
            return false;
        }

        // user's email must be in valid email format, also checks the length
        if (!filter_var($new_user_email, FILTER_VALIDATE_EMAIL))
        {
            Session::add('feedback_negative',
                         Text::get('FEEDBACK_EMAIL_DOES_NOT_FIT_PATTERN'));
            return false;
        }

        // strip tags, just to be sure
        $new_user_email = substr(strip_tags($new_user_email), 0, 254);

        // check if user's email already exists
        if (UserModel::doesEmailAlreadyExist($new_user_email))
        {
            Session::add('feedback_negative',
                         Text::get('FEEDBACK_USER_EMAIL_ALREADY_TAKEN'));
            return false;
        }

        // write to database, if successful ...
        if (UserModel::saveNewEmailAddress($user_id, $new_user_email))
        {
            Session::add('feedback_positive',
                         Text::get('FEEDBACK_EMAIL_CHANGE_SUCCESSFUL'));
            return true;
        }

        Session::add('feedback_negative', Text::get('FEEDBACK_UNKNOWN_ERROR'));
        return false;
    }

    public static function changeUsernameAndEmail($user_id)
    {
        $user = self::getUser($user_id);

        $newUserName = Request::post('userName', true);
        if ($newUserName != $user->user_name) {
            UserModel::editUserName($user_id, $newUserName);
        }

        $newUserEmail = Request::post('userEmail', true);
        if ($newUserEmail != $user->user_email) {
            UserModel::editUserEmail($user_id, $newUserEmail);
        }
    }

    /**
     * Gets the user's id
     *
     * @param $user_name
     *
     * @return mixed
     */
    public static function getUserIdByUsername($user_name)
    {
        $prefix = Config::get('DB_PREFIX');
        $database = DatabaseFactory::getFactory()->getConnection();

        $sql = "SELECT user_id
                FROM ".$prefix."users
                WHERE user_name = :user_name
                LIMIT 1";
        $query = $database->prepare($sql);

        $query->execute(array(':user_name' => $user_name));

        // return one row (we only have one result or nothing)
        return $query->fetch()->user_id;
    }

    /**
     * Gets the user's data
     *
     * @param $user_name string User's name
     *
     * @return mixed Returns false if user does not exist,
     * returns object with user's data when user exists
     */
    public static function getUserDataByUsername($user_name)
    {
        $prefix = Config::get('DB_PREFIX');
        $database = DatabaseFactory::getFactory()->getConnection();

        $sql = "SELECT user_id,
                       user_name,
                       user_email,
                       user_password_hash,
                       user_active,
                       user_approved,
                       user_is_admin,
                       user_failed_logins,
                       user_last_failed_login
                 FROM ".$prefix."users
                 WHERE (user_name = :user_name
                    OR user_email = :user_name)
                 LIMIT 1";
        $query = $database->prepare($sql);

        $query->execute(array(':user_name' => $user_name));

        // return one row (we only have one result or nothing)
        return $query->fetch();
    }

    /**
     * Gets the user's data by user's id and a token (used by login-via-cookie process)
     *
     * @param $user_id
     * @param $token
     *
     * @return mixed Returns false if user does not exist,
     * returns object with user's data when user exists
     */
    public static function getUserDataByUserIdAndToken($user_id, $token)
    {
        $prefix = Config::get('DB_PREFIX');
        $database = DatabaseFactory::getFactory()->getConnection();

        // get real token from database (and all other data)
        $query = $database->prepare("SELECT user_id,
                                            user_name,
                                            user_email,
                                            user_password_hash,
                                            user_active,
                                            user_is_admin,
                                            user_failed_logins,
                                            user_last_failed_login
                                     FROM ".$prefix."users
                                     WHERE user_id = :user_id
                                       AND user_remember_me_token = :user_remember_me_token
                                       AND user_remember_me_token IS NOT NULL
                                     LIMIT 1");
        $query->execute(array(':user_id' => $user_id,
                              ':user_remember_me_token' => $token));

        // return one row (we only have one result or nothing)
        return $query->fetch();
    }

    /**
    * approve the user
    *
    * @param $user_id
    *
    * @return boolean Returns false if user approval failed
    */
    public static function approveUser($user_id)
    {
        $prefix = Config::get('DB_PREFIX');
        $database = DatabaseFactory::getFactory()->getConnection();

        $query = $database->prepare("UPDATE ".$prefix."users
                                     SET user_approved = 1
                                     WHERE user_id = :user_id
                                     LIMIT 1");
        $query->bindParam(':user_id',    $user_id,        PDO::PARAM_INT);
        if ($query->execute() && $query->rowCount() > 0) {
            Session::add('feedback_positive',
                         AdminText::get('FEEDBACK_USER_APPROVAL_SUCCESSFUL'));
            $mail = new Mail;
            $mail_sent = $mail->sendMail(UserModel::getUser($user_id)->user_email,
                                         Config::getFromDB('MarketContactEmail'),
                                         Config::getFromDB('MarketName'),
                                         PA::filter(Text::get('EMAIL_APPROVAL_SUBJECT'),
                                                    Config::getFromDB('MarketName')),
                                         PA::filter(Text::get('EMAIL_APPROVAL_BODY'),
                                                    PA::getLink('login')));
            return true;
        } else {
            Session::add('feedback_negative',
                         AdminText::get('FEEDBACK_USER_APPROVAL_FAILED'));
            return false;
        }
    }

    /**
    * disapprove the user
    *
    * @param $user_id
    *
    * @return boolean Returns false if user disapproval failed
    */
    public static function disapproveUser($user_id)
    {
        $prefix = Config::get('DB_PREFIX');
        $database = DatabaseFactory::getFactory()->getConnection();

        $query = $database->prepare("UPDATE ".$prefix."users
                                     SET user_approved = 0
                                     WHERE user_id = :user_id
                                     LIMIT 1");
        $query->bindParam(':user_id',    $user_id,        PDO::PARAM_INT);
        if ($query->execute() && $query->rowCount() > 0) {
            Session::add('feedback_positive',
                         AdminText::get('FEEDBACK_USER_DISAPPROVAL_SUCCESSFUL'));
            return true;
        } else {
            Session::add('feedback_negative',
                         AdminText::get('FEEDBACK_USER_DISAPPROVAL_FAILED'));
            return false;
        }
    }

    public static function changePassword($user_id, $password_old, $password_new, $password_repeat)
    {
        if (!self::checkOldPassword($user_id, $password_old)) {
            Session::add('feedback_negative',
                         Text::get('FEEDBACK_OLD_PASSWORD_WRONG'));
            return false;
        }

        if ($password_new != $password_repeat) {
            Session::add('feedback_negative',
                         Text::get('FEEDBACK_PASSWORD_REPEAT_WRONG'));
            return false;
        }

        $usr_passwd_hash = password_hash($password_new, PASSWORD_DEFAULT);
        $prefix = Config::get('DB_PREFIX');
        $database = DatabaseFactory::getFactory()->getConnection();

        $query = $database->prepare("UPDATE ".$prefix."users
                                     SET user_password_hash = :passwd_hash
                                     WHERE user_id = :user_id
                                     LIMIT 1");
        $query->bindParam(':user_id',     $user_id,         PDO::PARAM_INT);
        $query->bindParam(':passwd_hash', $usr_passwd_hash, PDO::PARAM_STR);
        if ($query->execute() && $query->rowCount() > 0) {
            return true;
        } else {
            Session::add('feedback_negative',
                         Text::get('FEEDBACK_PASSWORD_CHANGE_FAILED'));
            return false;
        }
    }

    public static function checkOldPassword($user_id, $old_password)
    {
        if (Session::userIsAdmin()) {
            return true;
        }
        $prefix = Config::get('DB_PREFIX');
        $database = DatabaseFactory::getFactory()->getConnection();

        $query = $database->prepare("SELECT user_password_hash
                                     FROM ".$prefix."users
                                     WHERE user_id = :user_id
                                     LIMIT 1");
        $query->bindParam(':user_id',     $user_id,         PDO::PARAM_INT);
        $query->execute();

        // return one row (we only have one result or nothing)
        $result = $query->fetch();

        if (password_verify($old_password, $result->user_password_hash)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Deletes the User permanently
     *
     * @param $user_id
     *
     * @return boolean Returns false if user deletion failed
     */
    public static function deleteUser($user_id, $feedback = false)
    {
        $prefix = Config::get('DB_PREFIX');
        $database = DatabaseFactory::getFactory()->getConnection();

        $query = $database->prepare("DELETE FROM ".$prefix."users
                                     WHERE user_id = :user_id
                                     LIMIT 1");
        $query->bindParam(':user_id',    $user_id,        PDO::PARAM_INT);
        if ($query->execute() && $query->rowCount() > 0) {
            if ($feedback === true) {
                Session::add('feedback_positive',
                             AdminText::get('FEEDBACK_PROFILE_DELETION_SUCCESSFUL'));
            }
            return true;
        } else {
            if ($feedback === true) {
                Session::add('feedback_negative',
                             AdminText::get('FEEDBACK_PROFILE_DELETION_FAILED'));
            }
            return false;
        }
    }
}
