<?php
/********************************************************************************
 * (c)1337 aheadware.com - All rights reserved
 ********************************************************************************/


class TB_Server_Control_Auth {

    /**
     * Logs user in and creates appropriate session
     *
     * @param array $params ( 'email' => (string), 'pass' => (string), 'is_hashed' => true (1) | false (0) )
     *
     * @return TB_Server_Core_Response
     */
    public static function login( TB_Server_Core_RequestData $params ) {
        // Return value
        $resp = new TB_Server_Core_Response();

        // Validate params
        $email = $params->get( 'email' );
        $pass = $params->get( 'pass' );
        $isHashed = (bool)$params->get( 'is_hashed', false );
        if ( !$isHashed ) {
            $pass = TB_Shared_Utils_Security::getSaltedPassword( $pass );
        }

        // Get account
        $account = TB_Shared_Ent_Core_Account::getByCredentials( $email, $pass );
        if ( $account instanceof TB_Shared_Ent_Core_Account && false == $account->is_validated ) {
            $resp->addData( 'error', 'not_validated' );
            $ev = TB_Shared_Ent_Core_EmailValidation::getByEmail( $email );
            if ( is_null( $ev ) ) {
                $ev = new TB_Shared_Ent_Core_EmailValidation();
                $ev->account_id = $account->id;
                $ev->email = $email;
                $ev->token = uniqid( 'vm' );
            }
            $ev->state = TB_Shared_Ent_Core_EmailValidation::STATE_PENDING;
            $ev->save();

            $account = NULL;
        }
        $profile = NULL;

        // Login successful
        if ( $account instanceof TB_Shared_Ent_Core_Account && true == $account->is_validated ) {

            // Get latest profile for account
            $profiles = TB_Shared_Ent_TeamData_Profile::getByAccountId( $account->id );
            $teams = array();
            if ( !is_array( $profiles ) || 0 === count( $profiles ) ) {
                throw new Exception( "Account Id: " . $account->id . " does not have any profiles." );
            } else if ( 1 === count( $profiles )) {
                $profile = $profiles[0];
                array_push( $teams, TB_Shared_Ent_TeamData_Team::get( $profile->team_id ) );
            } else {
                // Multiple profiles
                $lastActive = 0;
                foreach( $profiles as $p ) {
                    $pt = TB_Shared_Ent_TeamData_ProfileTimestamp::getByProfileId( $p->id );
                    array_push( $teams, TB_Shared_Ent_TeamData_Team::get( $p->team_id ) );
                    if ( $lastActive < $pt->last_online ) {
                        $profile = $p;
                        $lastActive = $pt->last_online;
                    }
                }
            }

            // Create session
            /** @var TB_Shared_Ent_Core_Session $session */
            $session = new TB_Shared_Ent_Core_Session();
            $session->account_id = $account->id;
            $session->profile_id = $profile->id;
            $session->token = TB_Server_Core_Session::generateToken();
            $session->save();

            // Set session
            TB_Server_Core_Session::get()->init( $session );

            // Return entities to client
            $resp->addData( 'account', $account );
            $resp->addData( 'profile', $profile );
            $resp->addData( 'session', $session );
            $resp->addData( 'profiles', $profiles );
            $resp->addData( 'teams', $teams );
        }

        return $resp;
    }

    /**
     * Register a new account
     *
     * Trainer is required to validate email first. This method just prepares everything in the DB
     *
     * @param TB_Server_Core_RequestData $params ( 'email', 'pass', 'role' )
     * @return TB_Server_Core_Response
     */
    public static function registerTrainer( TB_Server_Core_RequestData $params ) {
        // Return value
        $resp = new TB_Server_Core_Response();

        // Default Params
        $teamName = $params->get( 'team_name' );
        $teamName = TB_Shared_Utils_Validator::isValidName( $teamName ) ? $teamName : NULL;
        $teamCategory = $params->get( 'team_category' );
        $teamCategory = TB_Shared_Utils_Validator::isValidName( $teamCategory ) ? $teamCategory : NULL;
        $email = $params->get( 'email' );
        $email = TB_Shared_Utils_Validator::isValidEmail( $email ) ? $email : NULL;
        $pass = $params->get( 'pass' );
        $pass = TB_Shared_Utils_Validator::isValidPassword( $pass ) ? $pass : NULL;
        $pass = TB_Shared_Utils_Security::getSaltedPassword( $pass );
        $gender = $params->get( 'gender' );
        $gender = TB_Shared_Utils_Validator::isValidValue( TB_Shared_Ent_TeamData_Profile::getValidGender(), $gender ) ? $gender : TB_Shared_Ent_TeamData_Profile::GENDER_MALE;
        $firstName = $params->get( 'first_name' );
        $firstName = TB_Shared_Utils_Validator::isValidName( $firstName) ? $firstName: NULL;
        $lastName = $params->get( 'last_name' );
        $lastName = TB_Shared_Utils_Validator::isValidName( $lastName) ? $lastName: NULL;
        $subscribedNews = ( 'on' === $params->get( 'subscribed_news' ) ) ? 1 : 0;
        $teamId = NULL;
        $account = NULL;

        // If params seems to be valid
        if ( isset( $email ) && isset( $pass ) && isset( $firstName ) && isset( $lastName ) && isset( $teamName ) ) {
            try {
                /** @var PDO $dbh */
                $dbh = TB_Shared_Db_Core::get();
                $dbh->beginTransaction();

                // If not
                // Create a new one
                $account = new TB_Shared_Ent_Core_Account();
                $account->email = $email;
                // Since player always comes through email invitation - his email is automatically validated
                $account->is_validated = 0;
                $account->pass = $pass;
                $account->created_dt = new DateTime();
                $account->subscribed_news = $subscribedNews;
                $account->save();

                // Create team if role is TRAINER
                $team = new TB_Shared_Ent_TeamData_Team();
                $team->public_id = TB_Server_Utils_Helper::getToken( 32 );
                $team->display_name = _xss( $teamName );
                $team->category = _xss( $teamCategory );
                $team->active_premium_package = TB_Server_Core_Premium::PREMIUM_PACKAGE_PREMIUM100_FREE;
                $team->created_ts = time();
                $team->save();
                $teamId = $team->id;

                // Create Premium Free Log Entry
                TB_Server_Core_Premium::createFreemiumPremiumLogEntryForTeam( $team );

                // Create profile to account
                $profile = new TB_Shared_Ent_TeamData_Profile();
                $profile->team_id = $teamId;
                $profile->account_id = $account->id;
                $profile->role = TB_Shared_Ent_TeamData_Profile::ROLE_TRAINER;
                $profile->gender = _xss( $gender );
                $profile->first_name = _xss( $firstName );
                $profile->last_name = _xss( $lastName );
                $profile->save();

                // Create profile timestamp entity
                $profileTimestamp = new TB_Shared_Ent_TeamData_ProfileTimestamp();
                $profileTimestamp->profile_id = $profile->id;
                $profileTimestamp->registered = time();
                $profileTimestamp->last_online = time();
                $profileTimestamp->last_news_update = time();
                $profileTimestamp->save();

                // Set data to response
                $resp->addData( 'account', $account );
                $resp->addData( 'profile', $profile );

                // Send validation mail
                $evm = new TB_Shared_Ent_Core_EmailValidation();
                $evm->account_id = $account->id;
                $evm->email = $account->email;
                $evm->token = uniqid( 'vm' );
                $evm->state = TB_Shared_Ent_Core_EmailValidation::STATE_PENDING;
                $evm->save();

                // Success
                $dbh->commit();
            } catch ( Exception $e ) {
                // Error
                $dbh->rollBack();

                // EMail already exists
                if ( $e instanceof PDOException && 23000 == $e->getCode() ) {
                    $resp->setCode( TB_Server_Core_Response::CODE_GENERAL_ERROR );
                    $resp->addData( 'gen_err', 'email_already_exists' );
                    $resp->addNotification( new TB_Server_Utils_Notification( _lc( 'EMail existiert bereits.' ), TB_Server_Utils_Notification::TYPE_WARNING ) );
                }
                else {
                    $resp->setCode( TB_Server_Core_Response::CODE_GENERAL_ERROR );
                    TB_Server_Utils_Log::get()->log( $e );
                }
            }
        } else {
            $resp->setCode( TB_Server_Core_Response::CODE_INVALID_DATA );
        }

        return $resp;
    }

    /**
     * Register a new account
     *
     * Use Case:
     * User actively creates an account (e.g. via landing page - mostly manager|trainer).
     *
     * @param TB_Server_Core_RequestData $params ( 'token', 'pass'  )
     * @return TB_Server_Core_Response
     */
    public static function registerPlayer( TB_Server_Core_RequestData $params ) {
        // Return value
        $resp = new TB_Server_Core_Response();

        // Default Params
        $pass = $params->get( 'pass' );
        $token = $params->get( 'token' );
        $subscribedNews = ( 'on' === $params->get( 'subscribed_news' ) ) ? 1 : 0;
        $invitation = is_null( $token ) ? NULL : TB_Shared_Ent_Core_Invitation::getByToken( $token );
        $team = is_null( $invitation ) ? NULL : TB_Shared_Ent_TeamData_Team::get( $invitation->team_id );

        // Invitation could not be found
        if ( is_null( $invitation ) ) {
            $resp->addData( 'error', 'invalid-token' );
            return $resp;
        }

        $account = TB_Shared_Ent_Core_Account::getByEmail( $invitation->email );

        if ( is_null( $account ) ) {
            $pass = TB_Shared_Utils_Validator::isValidPassword( $pass ) ? $pass : NULL;
            $pass = TB_Shared_Utils_Security::getSaltedPassword( $pass );
        }

        // If params seems to be valid
        if ( isset( $invitation ) && ( isset( $account ) || isset( $pass ) ) && isset( $team )) {
            try {
                /** @var PDO $dbh */
                $dbh = TB_Shared_Db_Core::get();
                $dbh->beginTransaction();

                /** @var PDO $dbhTD */
                $dbhTD = TB_Shared_Db_TeamData::get();
                $dbhTD->beginTransaction();

                // If not
                if ( is_null( $account ) ) {
                    // Create a new one
                    $account = new TB_Shared_Ent_Core_Account();
                    $account->email = $invitation->email;
                    // Since player always comes through email invitation - his email is automatically validated
                    $account->is_validated = true;
                    $account->pass = $pass;
                    $account->created_dt = new DateTime();
                    $account->subscribed_news = $subscribedNews;
                    $account->save();
                }

                // Create profile to account
                $profile = new TB_Shared_Ent_TeamData_Profile();
                $profile->team_id = $team->id;
                $profile->account_id = $account->id;
                $profile->role = $invitation->role;
                $profile->first_name = _xss( $invitation->first_name );
                $profile->last_name = _xss( $invitation->last_name );
                $profile->save();

                // Create profile timestamp entity
                $profileTimestamp = new TB_Shared_Ent_TeamData_ProfileTimestamp();
                $profileTimestamp->profile_id = $profile->id;
                $profileTimestamp->registered = time();
                $profileTimestamp->last_online = time();
                $profileTimestamp->last_news_update = time();
                $profileTimestamp->save();

                // Automatically create a valid session
                $session = new TB_Shared_Ent_Core_Session();
                $session->account_id = $account->id;
                $session->profile_id = $profile->id;
                $session->token = TB_Server_Core_Session::generateToken();
                $session->save();

                // Set session
                TB_Server_Core_Session::get()->init( $session );

                $profiles = TB_Shared_Ent_TeamData_Profile::getByAccountId( $account->id );
                $teams = array();
                foreach( $profiles as $p ) {
                    array_push( $teams, TB_Shared_Ent_TeamData_Team::get( $p->team_id ) );
                }


                // Set data to response
                $resp->addData( 'account', $account );
                $resp->addData( 'profile', $profile );
                $resp->addData( 'session', $session );
                $resp->addData( 'profiles', $profiles );
                $resp->addData( 'teams', $teams );

                // Mark invitation as done
                $invitation->state = TB_Shared_Ent_Core_Invitation::STATE_ACCEPTED;
                $invitation->save();

                // Success
                $dbh->commit();
                $dbhTD->commit();
            } catch ( Exception $e ) {
                // Error
                $dbh->rollBack();
                $dbhTD->rollBack();

                // EMail already exists
                if ( $e instanceof PDOException && 23000 == $e->getCode() ) {
                    $resp->setCode( TB_Server_Core_Response::CODE_GENERAL_ERROR );
                    $resp->addNotification( new TB_Server_Utils_Notification( _lc( 'EMail existiert bereits.' ), TB_Server_Utils_Notification::TYPE_WARNING ) );
                }
                else {
                    $resp->setCode( TB_Server_Core_Response::CODE_GENERAL_ERROR );
                    TB_Server_Utils_Log::get()->log( $e );
                }
            }
        } else {
            $resp->setCode( TB_Server_Core_Response::CODE_INVALID_DATA );
        }

        return $resp;
    }

    public static function getInvitation( TB_Server_Core_RequestData $params ) {
        $resp = new TB_Server_Core_Response();

        $token = $params->get( 'token' );
        $invitation = is_null( $token ) ? NULL : TB_Shared_Ent_Core_Invitation::getByToken( $token );
        /** @var TB_Shared_Ent_TeamData_Team $team */
        $team = isset( $invitation ) ? TB_Shared_Ent_TeamData_Team::get( $invitation->team_id ) : NULL;
        if ( is_null( $invitation ) || is_null( $team ) ) {
            $resp->addData( 'token', NULL );
        } else {
            $account = TB_Shared_Ent_Core_Account::getByEmail( $invitation->email );
            // If account exists
            if ( isset( $account ) ) {
                // Hide password to client
                $account->pass = '';
            }
            $resp->addData( 'token', $token );
            $resp->addData( 'invitation', $invitation );
            $resp->addData( 'inviter', TB_Shared_Ent_TeamData_Profile::get( $invitation->inviter_profile_id ) );
            $resp->addData( 'team', $team );
            $resp->addData( 'account', $account );
        }

        return $resp;
    }

    /**
     * @param TB_Server_Core_RequestData $params
     * @return TB_Server_Core_Response
     */
    public static function requestPasswordReset( TB_Server_Core_RequestData $params ) {
        $resp = new TB_Server_Core_Response();

        $email = $params->get( 'email' );
        $account = TB_Shared_Ent_Core_Account::getByEmail( $email );
        if ( isset( $account ) ) {
            $prs = TB_Shared_Ent_Core_PasswordReset::getByAccountId( $account->id );
            if ( 0 < count( $prs ) ) {
                $resp->addData( 'res', 'already_sent' );
            } else {
                $pr = new TB_Shared_Ent_Core_PasswordReset();
                $pr->account_id = $account->id;
                $pr->email = $account->email;
                $pr->token = uniqid( "pr" );
                $pr->state = TB_Shared_Ent_Core_PasswordReset::STATE_PENDING;
                $pr->save();
                $resp->addData( 'res', 'ok' );
            }
        } else {
            $resp->addData( 'res', 'account_not_found' );
        }

        return $resp;
    }

    /**
     *
     * @param TB_Server_Core_RequestData $params
     * @return TB_Server_Core_Response
     */
    public static function resetPassword( TB_Server_Core_RequestData $params ) {
        $resp = new TB_Server_Core_Response();

        /** @var TB_Shared_Ent_Core_PasswordReset $entPasswordReset */
        $entPasswordReset = TB_Shared_Ent_Core_PasswordReset::getByToken( $params->get( 'token' ) );
        /** @var TB_Shared_Ent_Core_Account $account */
        $account = ( isset( $entPasswordReset ) ) ? TB_Shared_Ent_Core_Account::get( $entPasswordReset->account_id ) : NULL;

        // Validation
        $password1 = $params->get( 'password1' );
        $password2 = $params->get( 'password2' );

        // If everything is correct
        if ( isset( $account ) &&
             TB_Shared_Utils_Validator::isValidPassword( $password1 ) &&
            ( $password1 == $password2 ) ) {

            // Update account with new password
            $account->pass = TB_Shared_Utils_Security::getSaltedPassword( $password1 );
            $account->save();

            $resp->addData( 'res', 'ok' );

        } else {
            $resp->addData( 'res', 'nok' );
        }

        return $resp;
    }


    /**
     *
     * @param TB_Server_Core_RequestData $params
     * @return TB_Server_Core_Response
     */
    public static function validateEmail( TB_Server_Core_RequestData $params ) {
        $resp = new TB_Server_Core_Response();

        /** @var TB_Shared_Ent_Core_EmailValidation $entEmailValidation*/
        $entEmailValidation = TB_Shared_Ent_Core_EmailValidation::getByToken( $params->get( 'token' ) );
        /** @var TB_Shared_Ent_Core_Account $account */
        $account = ( isset( $entEmailValidation ) ) ? TB_Shared_Ent_Core_Account::get( $entEmailValidation->account_id ) : NULL;

        // If everything is correct
        if ( isset( $account ) ) {

            $dbh = $account->getDbh();
            try {
                $dbh->beginTransaction();

                // Update account with new password
                $account->is_validated = 1;
                $account->save();

                $dbh->commit();

                $resp->addData( 'res', 'ok' );

                if ( TB_Server_Core_Session::get()->isInitialized() ){
                    $onlineProfile = TB_Server_Core_Session::get()->getProfile();
                    if ( isset( $onlineProfile ) ) {
                        $resp->addData( 'profile', $onlineProfile );
                    }
                }

                // Send welcome mail to coach
                try {
                    TB_Server_Utils_Mail::sendWelcomeTeam( array( 'email' => $account->email ) );
                } catch( Exception $e ) {
                    // Fail silently
                    TB_Server_Utils_Log::get()->log( $e );
                }

            } catch( Exception $e ) {
                TB_Server_Utils_Log::get()->log( $e );
                $resp->addData( 'res', 'nok' );
            }

        } else {
            $resp->addData( 'res', 'nok' );
        }

        return $resp;
    }

    /**
     * @param TB_Server_Core_RequestData $params
     * @return TB_Server_Core_Response
     * @throws Exception
     */
    public static function confirmEmailChange( TB_Server_Core_RequestData $params ) {
        $resp = new TB_Server_Core_Response();
        $result = 'nok';

        $token = $params->get( 'token' );

        /** @var TB_Shared_Ent_Core_EmailChange $ec */
        $ec = TB_Shared_Ent_Core_EmailChange::getByToken( $token );
        if ( isset( $ec ) ) {

            $now = time();
            $until = $ec->valid_until_dt->getTimestamp();

            // If token is still valid
            if ( $now < $until ) {

                /** @var TB_Shared_Ent_Core_Account $account */
                $account = TB_Shared_Ent_Core_Account::get( $ec->account_id );
                $duplicateAccount = TB_Shared_Ent_Core_Account::getByEmail( $ec->new_email );
                if ( isset( $account ) && is_null( $duplicateAccount ) ) {
                    $account->email = $ec->new_email;
                    $account->save();

                    $result = 'ok';

                    $ec->delete();
                }
            } else {
                $result = 'timeup';
            }
        }

        $resp->addData( 'email_change_result', $result );

        return $resp;
    }

    /**
     * Logs user out of current session
     *
     * @return TB_Server_Core_Response
     */
    public static function logout( TB_Server_Core_RequestData $params ) {
        // Return value
        $resp = new TB_Server_Core_Response();

        // Remove device registration
        $account = TB_Server_Core_Session::get()->getAccount();
        $deviceId = $params->get( 'device_id' );
        if ( !is_null( $account ) && !is_null( $deviceId ) ) {
            /** @var TB_Shared_Ent_Core_DeviceReg $dr */
            $dr = TB_Shared_Ent_Core_DeviceReg::getByAccountIdAndRegId( $account->id, $deviceId );
            if ( isset( $dr ) ) {
                $dr->delete();
            }
        }

        // Destory session
        TB_Server_Core_Session::get()->destory();

        return $resp;
    }
} 