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


class TB_Server_Control_Team {

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

        $addPremium = $params->get( 'addPremium', false );

        $profile = TB_Server_Core_Session::get()->getProfile();
        if ( is_null( $profile ) ) {
            $resp->setCode( TB_Server_Core_Response::CODE_INVALID_DATA );
        } else {


            // Team
            $team = TB_Shared_Ent_TeamData_Team::get( $profile->team_id );
            $resp->addData( 'team', $team );

            // Invitations
            $is = TB_Shared_Ent_Core_Invitation::getByTeamId( $profile->team_id );
            $invitations = array();
            /** @var TB_Shared_Ent_Core_Invitation $i */
            foreach( $is as $i ) {
                array_push( $invitations, $i->asArray() );
            }
            $resp->addData( 'invitation', $invitations );

            // Profile
            $resp->addData( 'profile', $profile->asArray() );

            // Profiles
            $ps = TB_Shared_Ent_TeamData_Profile::getProfilesByTeamId( $profile->team_id );
            /** @var TB_Shared_Ent_TeamData_Profile $p */
            $profiles = array();
            foreach( $ps as $p ) {
                array_push( $profiles, $p->asArray() );
            }
            $resp->addData( 'profiles', $profiles );

            // If premium should be added
            if ( $addPremium )
            {
                $entLastPremiumLog = TB_Shared_Ent_TeamData_PremiumLog::getLastPremiumLogForTeam( $team );
                $resp->addData( 'premium_last', $entLastPremiumLog );
            }

        }

        return $resp;
    }

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

        $profile = TB_Server_Core_Session::get()->getProfile();
        if ( is_null( $profile ) ) {
            $resp->setCode( TB_Server_Core_Response::CODE_INVALID_DATA );
        } else {

            // Team
            $team = TB_Shared_Ent_TeamData_Team::get( $profile->team_id );
            $resp->addData( 'team', $team );

            if ( $params->get( 'including_members' ) )
            {
                $resp->addData( 'team_members', TB_Shared_Ent_TeamData_Profile::getProfilesByTeamId( $team->id ) );
            }

        }

        return $resp;
    }

    /**
     * Update current team
     *
     * @param TB_Server_Core_RequestData $params
     * @return TB_Server_Core_Response
     * @throws Exception
     */
    public static function update( TB_Server_Core_RequestData $params ) {
        $resp = new TB_Server_Core_Response();

        $team = TB_Server_Core_Session::get()->getTeam();
        if ( is_null( TB_Server_Core_Session::get()->getTeam() ) ) {
            throw new Exception( 'Player does not have a team.' );
        }

        $displayName = $params->get( 'display_name', $team->display_name );
        $category = $params->get( 'category', $team->category );
        $team->reg_no = _xss( $params->get( 'reg_no' ) );
        $team->display_name = _xss( $displayName );
        $team->category = _xss( $category );
        $team->save();

        return $resp;
    }

    /**
     * Update current team emblem
     *
     * @param TB_Server_Core_RequestData $params
     * @return TB_Server_Core_Response
     * @throws Exception
     */
    public static function updateEmblem( TB_Server_Core_RequestData $params ) {

        $resp = new TB_Server_Core_Response();

        // Validation
        $team = TB_Server_Core_Session::get()->getTeam();
        if ( is_null( TB_Server_Core_Session::get()->getTeam() ) ) {
            throw new Exception( 'Player does not have a team.' );
        }

        // Retrieve raw image data
        $slim = $params->get( 'slim' );
        if ( !isset( $slim ) ) {
            throw new Exception( 'No slim data found.' );
        }
        $slimData = json_decode( $slim, true );
        if ( !is_array( $slimData ) ) {
            throw new Exception( 'Invalid slim data.' );
        }
        $rawImgData = $slimData['output']['image'];

        // Upload to cloud
        $cloudinary = TB_Server_Utils_Cloudinary::get();

        if ( is_null( $rawImgData ) )  {

            // Delete emblem
            if ( !is_null( $team->emblem_cloud_id ) ) {
                $res = $cloudinary->delete( $team->emblem_cloud_id );
            }
            $team->emblem_url = NULL;
            $team->emblem_cloud_id = NULL;
            $team->save();

        } else {

            // Store old cloud id
            $oldEmblemCloudId = $team->emblem_cloud_id;

            // Set new emblem
            $res = $cloudinary->upload( $rawImgData, array( 'emblem', $team->id ) );
            if ( !is_array( $res ) || !isset( $res[ 'url' ] ) || !isset( $res[ 'public_id' ] ) ) {
                throw new Exception( 'Invalid response from Cloudinary.' );
            }
            // Update team
            $team->emblem_url = $res[ 'url' ];
            $team->emblem_cloud_id = $res[ 'public_id' ];
            $team->save();

            // If new img is saved - remove old from the cloud
            if ( !is_null( $oldEmblemCloudId ) ) {
                $res = $cloudinary->delete( $oldEmblemCloudId );
                if ( !is_array( $res ) || !isset( $res[ 'result' ] ) || "ok" != $res[ 'result' ] ) {
                    TB_Server_Utils_Log::get()->log( "Could not delete old team emblem: " . $oldEmblemCloudId . ' - ' . @json_encode( $res ) );
                }
            }

            // Trigger news & pn
            Francis_Core_EventHandler::get()->trigger(
                'team_emblem_updated', array(
                'profile' => TB_Server_Core_Session::get()->getProfile(),
                'team' => $team
            ));

        }

        // Add updated team data to response
        $resp->addData( 'team', $team );

        return $resp;
    }

    /**
     * Invite Player to team
     *
     * @param TB_Server_Core_RequestData $params
     * @return TB_Server_Core_Response
     */
    public static function invitePlayer( TB_Server_Core_RequestData $params ) {
        $resp = new TB_Server_Core_Response();

        $profile = TB_Server_Core_Session::get()->getProfile();

        $email = $params->get( 'player_invitation_email' );
        $firstName = $params->get( 'player_invitation_first_name' );
        $lastName = $params->get( 'player_invitation_last_name' );
        $role = $params->get( 'player_invitation_role' );

        // Validate role
        if ( !in_array( $role, TB_Shared_Ent_Core_Invitation::getValidRoles() ) ||
             $role == TB_Shared_Ent_Core_Invitation::ROLE_TRAINER ) {
            throw new Exception( 'Invalid role for invitation: ' . $role );
        }

        // Flags
        $emailAlreadyInTeam = false;
        $emailAlreadyInvited = false;

        // Check if email already has an profile in team
        $account = TB_Shared_Ent_Core_Account::getByEmail( $email );
        if ( isset( $account ) ) {
            $profiles = TB_Shared_Ent_TeamData_Profile::getByAccountId( $account->id );
            if ( is_array( $profiles ) ) {
                foreach( $profiles as $p ) {
                    if ( $p->team_id == $profile->team_id ) {
                        $emailAlreadyInTeam = true;
                        break;
                    }
                }
            }
        }

        // Check if email already is currently invited
        $invs = TB_Shared_Ent_Core_Invitation::getByTeamId( $profile->team_id );
        /** @var TB_Shared_Ent_Core_Invitation $inv */
        foreach( $invs as $inv ) {
            if ( $inv->email == $email ) {
                    $emailAlreadyInvited = true;
                    break;
                }
        }

        // Data invalid
        if ( !TB_Shared_Utils_Validator::isValidEmail( $email ) ||
             !TB_Shared_Utils_Validator::isValidName( $firstName ) ||
             !TB_Shared_Utils_Validator::isValidName( $lastName ) ) {
            $resp->setCode( TB_Server_Core_Response::CODE_INVALID_DATA );
        }
        else if ( true === $emailAlreadyInTeam ) {
            $resp->addData( 'error', 'email-already-in-team' );
        }
        else if ( true === $emailAlreadyInvited ) {
            $resp->addData( 'error', 'email-already-invited' );
        }
        else
        {
            $dbh = TB_Shared_Db_TeamData::get();
            try {
                $dbh->beginTransaction();
                $team = TB_Server_Core_Session::get()->getTeam();
                // Create invitation
                $invitation = new TB_Shared_Ent_Core_Invitation();
                $invitation->team_id = $team->id;
                $invitation->inviter_profile_id = $profile->id;
                $invitation->first_name = _xss( $firstName );
                $invitation->last_name = _xss( $lastName );
                $invitation->email = $email;
                $invitation->role = $role;
                $invitation->token = uniqid();
                $invitation->state = TB_Shared_Ent_Core_Invitation::STATE_SENDING;
                $invitation->save();

                $dbh->commit();

            } catch ( Exception $e ) {
                $dbh->rollBack();
                TB_Server_Utils_Log::get()->log( $e );
                $resp->setCode( TB_Server_Core_Response::CODE_GENERAL_ERROR );
            }
        }

        return $resp;
    }

    /**
     * Deletes an invitation
     *
     * @param TB_Server_Core_RequestData $params
     * @return TB_Server_Core_Response
     * @throws Exception
     */
     public static function deleteInvitation( TB_Server_Core_RequestData $params ) {
        $resp = new TB_Server_Core_Response();

         // Validation
         $profile = TB_Server_Core_Session::get()->getProfile();
         if ( !$profile->isTrainer() && !$profile->isCoTrainer() ) {
             throw new Exception( "Not allowed" );
         }

        /** @var TB_Shared_Ent_Core_Invitation $invitataion */
        $invitataion = TB_Shared_Ent_Core_Invitation::get( $params->get( 'invitation_id' ) );
        if ( isset( $invitataion ) && $invitataion->team_id == $profile->team_id ) {
            if ( $invitataion->state == TB_Shared_Ent_Core_Invitation::STATE_DELIVERED ||
                 $invitataion->state == TB_Shared_Ent_Core_Invitation::STATE_CANCELLED ) {
                $invitataion->delete();
                $resp->addData( 'res', 'ok' );
            }
        }

        return $resp;
    }

    /**
     * Resends existing invitation
     *
     * @param TB_Server_Core_Response $params
     * @return TB_Server_Core_Response
     */
    public static function resendInvitation( TB_Server_Core_RequestData $params ) {
        $resp = new TB_Server_Core_Response();

        // Validation
        $profile = TB_Server_Core_Session::get()->getProfile();
        if ( !$profile->isTrainer() && !$profile->isCoTrainer() ) {
            throw new Exception( "Not allowed" );
        }

        /** @var TB_Shared_Ent_Core_Invitation $invitataion */
        $invitataion = TB_Shared_Ent_Core_Invitation::get( $params->get( 'invitation_id' ) );
        if ( isset( $invitataion ) ) {
            if ( $invitataion->state == TB_Shared_Ent_Core_Invitation::STATE_DELIVERED &&
                $invitataion->last_update < ( time() - 86400 ) ) {
                $invitataion->state = TB_Shared_Ent_Core_Invitation::STATE_SENDING;
                $invitataion->save();
                $resp->addData( 'res', 'ok' );
            } else {
                $resp->addData( 'res', 'not-enough-time-passed' );
            }
        }

        return $resp;
    }

    /**
     * @param TB_Server_Core_RequestData $params
     * @return TB_Server_Core_Response
     */
    public static function getPremiumDetails( TB_Server_Core_RequestData $params )
    {
        $resp = new TB_Server_Core_Response();
        $customerData = $params->get( 'customerData' );

        // Check if current account also have a braintree customer id

        // If so load it

        // Else create a new one



        /*

        $customer = Braintree_Customer::create([
            'paymentMethodNonce' => $customerData[ 'nonce' ]
        ]);
        */


        /*
        $result = Braintree_Customer::create([
            paymentMethodNonce :
        ]);
        */


        return $resp;
    }

    /**
     * Create a new premium subscription
     *
     * @param TB_Server_Core_RequestData $params
     * @return TB_Server_Core_Response
     */
    public static function createPremium( TB_Server_Core_RequestData $params )
    {
        // Response object
        $resp = new TB_Server_Core_Response();

        /** @var TB_Shared_Ent_Core_Account $account */
        $account = TB_Server_Core_Session::get()->getAccount();
        /** @var TB_Shared_Ent_TeamData_Team $team */
        $team = TB_Server_Core_Session::get()->getTeam();

        // Resolve from client
        $braintreeCustomerData = $params->get( 'braintreeCustomerData' );
        $billingData = $params->get( 'billingData' );
        $packageId = $params->get( 'packageId' );

        // Premium data
        $planId = null;
        $planStartDt = null;

        $brainTreeFirstBillingDateUTC = null;

        $paypalPaymentMethod = isset( $braintreeCustomerData[ 'nonce' ] ) ? $braintreeCustomerData[ 'nonce' ] : null;

        // --- start: Validation

        if ( is_null( $account ) ||
             is_null( $team ) )
        {
            throw new Exception( 'PREMIUM: Invalid data.' );
        }

        // Check is valid package id
        if ( false === TB_Server_Core_Premium::isValidPackageId( $packageId ) )
        {
            throw new Exception( 'Given package id does not exists: ' . $packageId );
        }

        self::validateBillingData( $billingData );

        // ------ stop


        $planId = TB_Server_Core_Premium::getBraintreePlanId( $packageId );

        /** @var TB_Shared_Ent_TeamData_PremiumLog $entLastPrmeiumLog */
        $entLastPrmeiumLog = TB_Shared_Ent_TeamData_PremiumLog::getLastPremiumLogForTeam( $team );
        if ( ( $entLastPrmeiumLog instanceof TB_Shared_Ent_TeamData_PremiumLog ) &&
             is_null( $entLastPrmeiumLog->valid_until_dt ) )
        {
            throw new Exception( 'PREMIUM: There is already a running subscription.' );
        }

        // --- start: Calculate beginning and end of time span

        // Now
        $now = new DateTime();
        $now->setTime( 0, 0, 0 );
        $nowTs = $now->getTimestamp();

        // Calculate start of premium package
        $currentDt = ( $entLastPrmeiumLog instanceof TB_Shared_Ent_TeamData_PremiumLog ) ? $entLastPrmeiumLog->valid_until_dt : new DateTime( "yesterday" );
        $currentDt->modify( '+2 minutes' );
        $currentDt->setTime( 0, 0, 0 );
        $currentTs = $currentDt->getTimestamp();

        $diff = $nowTs - $currentTs;

        // If time is already up
        if ( $diff > 0 )
        {
            $planStartDt = $now;
        }
        else
        {
            $diffDays = (int)floor( $diff / ( 60 * 60 * 24 ) );
            $diffDays *= -1;
            $planStartDt = DateTime::createFromFormat(
                "Y-m-d H:i:s",
                date( "Y-m-d H:i:s", strtotime( "+$diffDays day", $nowTs ) )
            );

            $brainTreeFirstBillingDateUTC = clone $planStartDt;
            $brainTreeFirstBillingDateUTC->setTimezone( new DateTimeZone( 'UTC' ) );
            $brainTreeFirstBillingDateUTC->modify( '+12 hours' );
        }

        // ------ stop

        TB_Server_Core_Premium::initBraintree();

        // Create a new customer
        $cusomterRes = Braintree_Customer::create([
            'paymentMethodNonce' => $paypalPaymentMethod
        ]);

        // If so load it
        if ( $cusomterRes->success )
        {
            $paypalPaymentMethod = $cusomterRes->customer->paymentMethods[ 0 ]->token;

            // NOte:
            // Only set firstBillingDate, when it is in the future
            $subscriptionRes = Braintree_Subscription::create([
                'paymentMethodToken' => $paypalPaymentMethod,
                'planId' => $planId,
                'firstBillingDate' => $brainTreeFirstBillingDateUTC
            ]);

            // If everything went OK
            if ( $subscriptionRes->success )
            {
                try
                {
                    if ( $subscriptionRes->subscription )
                    {
                        $subscription  = $subscriptionRes->subscription;

                        $entPremiumLog = new TB_Shared_Ent_TeamData_PremiumLog();

                        // Owner
                        $entPremiumLog->braintree_customer_id = $cusomterRes->customer->id;
                        $entPremiumLog->braintree_subscription_id = $subscription->id;
                        $entPremiumLog->account_id = $account->id;
                        $entPremiumLog->team_id = $team->id;

                        // Package
                        $entPremiumLog->package_id = $packageId;
                        $entPremiumLog->package_price = TB_Server_Core_Premium::getPackagePrice( $packageId );
                        $entPremiumLog->package_tax = TB_Server_Core_Premium::getPackageTax( $packageId );
                        $entPremiumLog->valid_from_dt = $planStartDt;
                        $entPremiumLog->valid_until_dt = NULL;

                        // Billing data
                        $entPremiumLog->billing_company_name = $billingData[ 'company_name' ];
                        $entPremiumLog->billing_company_tax_id = $billingData[ 'tax_id' ];
                        $entPremiumLog->billing_first_name = $billingData[ 'first_name' ];
                        $entPremiumLog->billing_last_name = $billingData[ 'last_name' ];
                        $entPremiumLog->billing_street = $billingData[ 'street' ];
                        $entPremiumLog->billing_zip = $billingData[ 'zip_code' ];
                        $entPremiumLog->billing_city = $billingData[ 'city' ];
                        $entPremiumLog->billing_email= $billingData[ 'email' ];

                        // Status
                        $entPremiumLog->status = ( $subscription->status == Braintree_Subscription::PENDING ) ? TB_Shared_Ent_TeamData_PremiumLog::STATUS_PENDING : strtoupper( $subscription->status );

                        $entPremiumLog->save();
                    }

                    if ( TB_Server_Core_Premium::isPackageBetterThanCurrent( $team, $packageId ) )
                    {
                        $team->active_premium_package = $planId;
                        $team->save();
                    }

                    // @todo Send mail
                }
                catch ( Exception $e )
                {
                    TB_Server_Utils_Log::get()->log( 'PREMIUM: Could not update premium settings for team: ' . $team->id );
                    TB_Server_Utils_Log::get()->log( $e );
                }
            }
            else
            {
                $logger = TB_Server_Utils_Log::get();
                $logger->log( 'PREMIUM: Subscription create errors...' );

                foreach( $subscriptionRes->errors->deepAll() AS $error)
                {
                    $logger->log($error->attribute . ": " . $error->code . " " . $error->message );

                }

                foreach( $subscriptionRes->errors->forKey('customer')->shallowAll() AS $error)
                {
                    $logger->log($error->attribute . ": " . $error->code . " " . $error->message );
                }

                throw new Exception( 'PREMIUM: Subscription could not been created.' );
            }
        }

        $resp->addData( 'subscription', $subscriptionRes );
        $resp->addData( 'team', $team );

        return $resp;
    }

    /**
     * @param array $bData
     * @return bool
     */
    private static function validateBillingData( array $bData )
    {
        if ( !isset( $bData[ 'first_name' ] ) ) return false;
        if ( !isset( $bData[ 'last_name' ] ) ) return false;
        if ( !isset( $bData[ 'street' ] ) ) return false;
        if ( !isset( $bData[ 'zip_code' ] ) ) return false;
        if ( !isset( $bData[ 'city' ] ) ) return false;
        if ( !isset( $bData[ 'email' ] ) ) return false;

        return true;
    }

}