<?php

namespace app\controllers;

use Yii;
use yii\data\ActiveDataProvider;
use yii\web\Controller;
use yii\web\Response;
use app\models\Campaign;
use app\models\CampaignSearch;
use app\models\CampaignRedirection;
use yii\mongodb\Query;
use app\models\Form;
use app\components\Utilities;
use app\components\FormHelpers;
use app\models\Node;
use yii\web\NotFoundHttpException;

class CampaignsController extends Controller
{
    public function behaviors()
    {
        return [
            'access' => [
                'class' => \yii\filters\AccessControl::class,
                'rules' => [
                    [
                        'allow' => true,
                        'actions' => ['public-form'],
                        'roles' => ['?', '@']
                    ],
                    [
                        'allow' => true,
                        'roles' => ['Admin']
                    ],
                    [
                        'allow' => true,
                        'actions' => ['index', 'get-generated-link', 'uncompleted-data', 'get-uncompleted-data-records'],
                        'roles' => ['User']
                    ],
                ],
                'denyCallback' => function(){
                    if(Yii::$app->user->isGuest){
                        return Yii::$app->response->redirect(['/users/login']);
                    }
                    return Yii::$app->response->redirect(Yii::$app->homeUrl);
                }
            ],
        ];
    }

    public function actionIndex(){
        $searchModel = new CampaignSearch();
        $dataProvider = $searchModel->search(Yii::$app->request->queryParams);

        return $this->render('index', [
            'dataProvider' => $dataProvider,
            'searchModel' => $searchModel,
        ]);
    }

    public function actionView($id){
        $model = $this->findModel($id);
        
        return $this->render('view', [
            'model' => $model,
            'can_activate' => Utilities::canActivateCampaign($id),
        ]);
    }
    
    protected function findModel($id){
        if(($model = Campaign::findOne($id)) !== null){
            return $model;
        }

        throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.'));
    }

    public function actionCreate($form_id){
        $model = new Campaign();
        $model->formId = Utilities::getIdAsObject($form_id);

        if (Yii::$app->request->isAjax) {
            $model->load(Yii::$app->request->post());
            Yii::$app->response->format = Response::FORMAT_JSON;
            return \yii\widgets\ActiveForm::validate($model);
        }

        if($model->load(Yii::$app->request->post())){
            $data = Yii::$app->request->post();
            $model->createdAt = Utilities::getDateTime();
            $model->status = 'Active';
            $model->destinationNodeId = Node::findOne(['name' => $model->destinationNodeId])->_id;
            $model->formVersion = Utilities::getFormActiveVersion($form_id);
            $model->campaignFields = array_keys($data['CampaignFields']);
            $model->dataCompletionFields = isset($data['DataCompletionFields']) ? array_keys($data['DataCompletionFields']) : [];
            
            // ID fields must be exist in campaign fields, so we will append it to campaign fields
            $ID_fields = Utilities::getIDFieldsOfForm($form_id, $model->formVersion);
            $model->campaignFields = array_merge($ID_fields, $model->campaignFields);

            if($model->save()){
                $model->generatedLink = '/campaigns/public-form?campaign_id=' . (string)$model->_id;
                if($model->save()){
                    return $this->redirect(['view', 'id' => (string)$model->_id]);
                }
            }
        }

        $form_fields = (new Query())
            ->from('formsVersions')
            ->select(['fields', 'versionNumber'])
            ->where([
                'formId' => $model->formId,
                'status' => 'Active'
            ])
            ->one();
        
        $fields = [];
        $show_if_fields = [];
        $related_fields = [];
        foreach($form_fields['fields'] as $field){
            if(isset($field['showIfFieldName'])){
                // Add as key => value in order to manipulate it later
                $show_if_fields[$field['name']] = $field['showIfFieldName'];
            }
            if(isset($field['relatedListFieldName'])){
                // Add as key => value in order to manipulate it later
                $related_fields[$field['name']] = $field['relatedListFieldName'];
            }

            // The ID fields must be exist in campaign fields
            if($field['type'] != 'id'){
                $fields[] = [
                    'name' => $field['name'],
                    'label' => $field['label'],
                    'type' => $field['type'],
                    'is_required' => isset($field['required']) && $field['required'],
                ];
            }
        }

        // Loop throughout the related fields in order to assign "all" children lists to the parent list
        // Countries => [Cities, Regions, Neighborhoods]
        // Cities => [Regions, Neighborhoods]
        // Regions => [Neighborhoods]
        $handled_related_lists = [];
        foreach($related_fields as $related_field_value){
            $loop = true;
            $name = $related_field_value;
            while($loop){
                foreach($related_fields as $temp_related_field_key => $temp_related_field_value){
                    if($temp_related_field_value == $name){
                        $handled_related_lists[$related_field_value][] = $temp_related_field_key;
                        $name = $temp_related_field_key;
                    }
                    else{
                        $loop = false;
                    }
                }
            }
        }

        $handled_show_if_fields = [];
        foreach($show_if_fields as $show_if_field_value){
            $loop = true;
            $name = $show_if_field_value;
            while($loop){
                foreach($show_if_fields as $temp_show_if_field_key => $temp_show_if_field_value){
                    if($temp_show_if_field_value == $name){
                        $handled_show_if_fields[$show_if_field_value][] = $temp_show_if_field_key;
                        $name = $temp_show_if_field_key;
                    }
                    else{
                        $loop = false;
                    }
                }
            }
        }
        
        return $this->render('create', [
            'model' => $model,
            'form_name' => Utilities::getFormName($form_id),
            'fields' => $fields,
            'show_if_fields' => Utilities::customJsonEncode($handled_show_if_fields),
            'related_fields' => Utilities::customJsonEncode($handled_related_lists),
        ]);
    }

    public function actionUpdate($id){
        $model = $this->findModel($id);

        if (Yii::$app->request->isAjax) {
            $model->load(Yii::$app->request->post());
            Yii::$app->response->format = Response::FORMAT_JSON;
            return \yii\widgets\ActiveForm::validate($model);
        }

        if($model->load(Yii::$app->request->post())){
            $data = Yii::$app->request->post();
            $model->createdAt = Utilities::getDateTime();
            $model->status = 'Active';
            $model->destinationNodeId = Node::findOne(['name' => $model->destinationNodeId])->_id;
            $model->formVersion = Utilities::getFormActiveVersion((string)$model->formId);
            $model->campaignFields = array_keys($data['CampaignFields']);
            $model->dataCompletionFields = isset($data['DataCompletionFields']) ? array_keys($data['DataCompletionFields']) : [];
            if($model->save()){
                return $this->redirect(['view', 'id' => (string)$model->_id]);
            }
        }

        $form_fields = (new Query())
            ->from('formsVersions')
            ->select(['fields', 'versionNumber'])
            ->where([
                'formId' => $model->formId,
                'status' => 'Active'
            ])
            ->one();
        
        $fields = [];
        $show_if_fields = [];
        $related_fields = [];
        foreach($form_fields['fields'] as $field){
            if(isset($field['showIfFieldName'])){
                // Add as key => value in order to manipulate it later
                $show_if_fields[$field['name']] = $field['showIfFieldName'];
            }
            if(isset($field['relatedListFieldName'])){
                // Add as key => value in order to manipulate it later
                $related_fields[$field['name']] = $field['relatedListFieldName'];
            }

            $fields[] = [
                'name' => $field['name'],
                'label' => $field['label'],
                'type' => $field['type'],
                'is_required' => isset($field['required']) && $field['required'],
            ];
        }

        $handled_related_lists = [];
        foreach($related_fields as $related_field_value){
            $loop = true;
            $name = $related_field_value;
            while($loop){
                foreach($related_fields as $temp_related_field_key => $temp_related_field_value){
                    if($temp_related_field_value == $name){
                        $handled_related_lists[$related_field_value][] = $temp_related_field_key;
                        $name = $temp_related_field_key;
                    }
                    else{
                        $loop = false;
                    }
                }
            }
        }

        $handled_show_if_fields = [];
        foreach($show_if_fields as $show_if_field_value){
            $loop = true;
            $name = $show_if_field_value;
            while($loop){
                foreach($show_if_fields as $temp_show_if_field_key => $temp_show_if_field_value){
                    if($temp_show_if_field_value == $name){
                        $handled_show_if_fields[$show_if_field_value][] = $temp_show_if_field_key;
                        $name = $temp_show_if_field_key;
                    }
                    else{
                        $loop = false;
                    }
                }
            }
        }

        return $this->render('update', [
            'model' => $model,
            'form_name' => Utilities::getFormName((string)$model->formId),
            'fields' => $fields,
            'show_if_fields' => Utilities::customJsonEncode($handled_show_if_fields),
            'related_fields' => Utilities::customJsonEncode($handled_related_lists),
        ]);
    }

    public function actionShowCreateCampaignModal(){
        $model = new Campaign();

        if($model->load(Yii::$app->request->post())){
            if($model->formId){
                return $this->redirect(['create', 'form_id' => $model->formId]);
            }
        }

        $forms = Form::find()
            ->where(['status' => 'Active'])
            ->all();
        $forms_data = [];
        if($forms){
            foreach($forms as $form){
                $forms_data[] = [
                    'id' => (string)$form['_id'],
                    'name' => $form['name']
                ];
            }
        }
        
        return $this->renderAjax('create-campaign-modal', [
            'forms' => $forms_data,
            'model' => $model
        ]);
    }

    public function actionGetGeneratedLink($id){
        $model = $this->findModel($id);

        return $this->renderAjax('generated-link-modal', [
            'model' => $model,
        ]);
    }

    public function actionCollectedData($campaign_id){
        $model = $this->findModel($campaign_id);
        
        if(! Yii::$app->user->can('Admin')){
            return $this->redirect(['index']);
        }

        $version = (new Query())
            ->from('formsVersions')
            ->where([
                'formId' => $model->formId,
                'versionNumber' => $model->formVersion,
            ])
            ->one();

        if($version){
            $columns = [];

            foreach($model->campaignFields as $campaign_field){
                if(! Utilities::isMatrix($campaign_field) && ! Utilities::isHeader($campaign_field) && ! Utilities::isParagraph($campaign_field) && ! Utilities::isFile($campaign_field)){
                    foreach($version['fields'] as $field){
                        if($field['name'] == $campaign_field){
                            $columns[$field['name']] = $field['label'];
                        }
                    }
                }
            }

            // In all collected data, we get all fields even if itsn't filled-out 
            foreach($model->dataCompletionFields as $data_completion_field){
                if(! Utilities::isMatrix($data_completion_field) && ! Utilities::isHeader($data_completion_field) && ! Utilities::isParagraph($data_completion_field) && ! Utilities::isFile($data_completion_field)){
                    foreach($version['fields'] as $field){
                        if($field['name'] == $data_completion_field){
                            $columns[$field['name']] = $field['label'];
                        }
                    }
                }
            }
        }

        return $this->render('collected-data', [
            'columns_names' => Utilities::customJsonEncode(array_keys($columns)),
            'columns_labels' => array_values($columns),
            'model' => $model,
        ]);
    }

    public function fillData($fields, $submitted_item, $form_id, &$row_data){
        foreach($fields as $field){
            if(Utilities::isLookup($field)){
                if(isset($submitted_item[$field])){
                    $lookup_data = Utilities::getLookupFieldData((string)$form_id, $field, $submitted_item[$field]);
                    $row_data[] = Utilities::getDataTableViewRecordLink($lookup_data['form'], $lookup_data['document']['_id'], $submitted_item[$field]);
                }
                else{
                    $row_data[] = Utilities::getNotSetSpan();
                }
            }
            elseif(! Utilities::isMatrix($field) && ! Utilities::isHeader($field) && ! Utilities::isParagraph($field) && ! Utilities::isFile($field)){
                $row_data[] = $submitted_item[$field] ?? Utilities::getNotSetSpan();
            }
        }
    }

    public function actionGetCollectedDataRecords($campaign_id, $start, $length){
        $model = $this->findModel($campaign_id);
        $columns = Yii::$app->request->get()['columns'];
        $search_and_sort = Utilities::handleDataTableSearchAndSort($columns, Yii::$app->request->get()['order'] ?? null);
        $search_values = $search_and_sort['search'];
        $sort_values = $search_and_sort['sort'];
        $filters = Utilities::getDataFiltersAccordingToActiveNode($search_values);

        $res = (new Query())
            ->from('forms_' . (string)$model->formId)
            ->select(Utilities::getProjectionFields(['campaign_id' => 0, 'isCompleted' => 0]))
            ->where([
                'campaignId' => Utilities::getIdAsObject($campaign_id),
            ])
            ->andWhere($filters)
            ->orderBy($sort_values)
            ->offset($start)
            ->limit($length)
            ->all();

        $data = [];
        if($res){
            foreach($res as $item){
                $row_data = [];
                $row_data[] = Utilities::getDataTableCompeleteRecordLink($campaign_id, $item['_id']);
                $this->fillData($model->campaignFields, $item, $model->formId, $row_data);
                // In all collected data, we get all fields even if itsn't filled-out
                $this->fillData($model->dataCompletionFields, $item, $model->formId, $row_data);
                $data[] = $row_data;
            }
        }
                
        $count = (new Query())
            ->from('forms_' . (string)$model->formId)
            ->where([
                'campaignId' => Utilities::getIdAsObject($campaign_id),
            ])
            ->andWhere($filters)
            ->count();

        return Utilities::customJsonEncode([
            'recordsTotal' => $count,
            'recordsFiltered' => $count,
            'data' => $data
        ]);
    }

    public function actionUncompletedData($campaign_id){
        $model = $this->findModel($campaign_id);
        // Check permission
        // The user can only view uncompleted-data of the campaigns those related to forms in his/her assigned groups
        if(! Yii::$app->user->can('Admin')){
            if(! Utilities::checkPermissionForForm((string)$model->formId, 'complete-data')){
                return $this->redirect(['index']);
            }
        }

        $version = (new Query())
            ->from('formsVersions')
            ->where([
                'formId' => $model->formId,
                'versionNumber' => $model->formVersion,
            ])
            ->one();

        if($version){
            $columns = [];

            foreach($model->campaignFields as $campaign_field){
                if(! Utilities::isMatrix($campaign_field) && ! Utilities::isHeader($campaign_field) && ! Utilities::isParagraph($campaign_field) && ! Utilities::isFile($campaign_field)){
                    foreach($version['fields'] as $field){
                        if($field['name'] == $campaign_field){
                            $columns[$field['name']] = $field['label'];
                        }
                    }
                }
            }
        }

        return $this->render('uncompleted-data', [
            'columns_names' => Utilities::customJsonEncode(array_keys($columns)),
            'columns_labels' => array_values($columns),
            'model' => $model,
        ]);
    }

    public function actionGetUncompletedDataRecords($campaign_id, $start, $length){
        $model = $this->findModel($campaign_id);
        $columns = Yii::$app->request->get()['columns'];
        $search_and_sort = Utilities::handleDataTableSearchAndSort($columns, Yii::$app->request->get()['order'] ?? null);
        $search_values = $search_and_sort['search'];
        $sort_values = $search_and_sort['sort'];
        $filters = Utilities::getDataFiltersAccordingToActiveNode($search_values);

        $res = (new Query())
            ->from('forms_' . (string)$model->formId)
            ->select(Utilities::getProjectionFields(['campaign_id' => 0, 'isCompleted' => 0]))
            ->where([
                'campaignId' => Utilities::getIdAsObject($campaign_id),
                'isCompleted' => false,
            ])
            ->andWhere($filters)
            ->orderBy($sort_values)
            ->offset($start)
            ->limit($length)
            ->all();

        $data = [];
        if($res){
            foreach($res as $item){
                $row_data = [];
                $row_data[] = Utilities::getDataTableCompeleteRecordLink($campaign_id, $item['_id']);
                $this->fillData($model->campaignFields, $item, $model->formId, $row_data);
                $data[] = $row_data;
            }
        }
        
        $count = (new Query())
            ->from('forms_' . (string)$model->formId)
            ->where([
                'campaignId' => Utilities::getIdAsObject($campaign_id),
                'isCompleted' => false,
            ])
            ->andWhere($filters)
            ->count();

        return Utilities::customJsonEncode([
            'recordsTotal' => $count,
            'recordsFiltered' => $count,
            'data' => $data
        ]);
    }

    public function actionChangeStatus($id, $action){
        if(Yii::$app->request->isPost){
            $collection = Yii::$app->mongodb->getCollection('campaigns');

            if($action == 'Activate' && ! Utilities::canActivateCampaign($id)){
                return $this->redirect(['view', 'id' => $id]);
            }

            $data = [];
            switch($action){
                case 'pause':
                    $data = [
                        '$set' => [
                            'status' => 'Paused',
                        ],
                        '$push' => [
                            'pausedBy' => Yii::$app->user->identity->_id,
                            'pausedAt' => Utilities::getDateTime(),
                        ],
                    ];
                break;
                case 'activate':
                    $data = [
                        '$set' => [
                            'status' => 'Active',
                        ],
                        '$push' => [
                            'activatedBy' => Yii::$app->user->identity->_id,
                            'activatedAt' => Utilities::getDateTime(),
                        ],
                    ];
                break;
                case 'complete':
                    $data = [
                        'status' => 'Ended',
                        'endedBy' => Yii::$app->user->identity->_id,
                        'endedAt' => Utilities::getDateTime(),
                    ];
                break;
            }

            $collection->update([
                '_id' => Utilities::getIdAsObject($id),
            ], $data);

            return $this->redirect(['view', 'id' => $id]);
        }
    }

    public function actionExtendCampaign($id){
        $model = $this->findModel($id);
        
        if($model->load(Yii::$app->request->post())){
            $collection = Yii::$app->mongodb->getCollection('campaigns');
            
            $data = [
                '$set' => [
                    'hasExtended' => true,
                    'endDate' => Yii::$app->request->post()['Campaign']['endDate']
                ],
                '$push' => [
                    'extendedBy' => Yii::$app->user->identity->_id,
                    'extendedAt' => Utilities::getDateTime(),
                ],
            ];

            $collection->update([
                '_id' => Utilities::getIdAsObject($id),
            ], $data);

            return $this->redirect(['view', 'id' => $id]);
        }

        return $this->renderAjax('extend-campaign-modal', [
            'model' => $model,
        ]);
    }

    // Redirect campaign to another campaign
    // When user visits campaign link, if it has been redirected to another campaign, and if the redirection still active,
    // the system will get the link of the destination campaign in order to display it instead of source campaign
    public function actionAddRedirect(){
        if(Yii::$app->request->isPost){
            $data = Yii::$app->request->post();
            $source_campaign_id = $data['source-campaign'];
            $destination_campaign_id = $data['destination-campaign'];

            $source_campaign = $this->findModel($source_campaign_id);
            $destination_campaign = $this->findModel($destination_campaign_id);

            $source_campaign_old_status = $source_campaign->status;
            $source_campaign->status = 'Terminated';
            $source_campaign->mustRedirectTo = $destination_campaign->generatedLink;
            $source_campaign->redirectionStatus = 'Enabled';
            if($source_campaign->save()){
                $collection = Yii::$app->mongodb->getCollection('campaignsRedirections');
                $result = $collection->insert([
                    'sourceCampaignId' => Utilities::getIdAsObject($source_campaign_id),
                    'sourceCampaignStatus' => $source_campaign_old_status,
                    'destinationCampaignId' => Utilities::getIdAsObject($destination_campaign_id),
                    'sourceCampaignLink' => $source_campaign->generatedLink,
                    'destinationCampaignLink' => $source_campaign->mustRedirectTo,
                    'redirectedBy' => Yii::$app->user->identity->_id,
                    'redirectedAt' => Utilities::getDateTime(),
                ]);

                if($result){
                    Yii::$app->session->setFlash('campaigns_redirections_completed', 'The redirection between campaigns has been done successfully');
                    return $this->redirect(['index']);
                }
            }
        }

        $source_campaigns_models = Campaign::find()
            ->where([
                '$or' => [
                    ['status' => 'Active'],
                    ['status' => 'Paused'],
                    ['status' => 'Terminated'],
                ],
            ])
            ->all();
        
        $source_campaigns = [];
        if($source_campaigns_models){
            foreach($source_campaigns_models as $source_campaign_model){
                $source_campaigns[(string)$source_campaign_model->_id] = $source_campaign_model->name;
            }
        }

        return $this->render('add-redirect', [
            'source_campaigns' => $source_campaigns
        ]);
    }

    public function actionCampaignsRedirections(){
        $dataProvider = new ActiveDataProvider([
            'query' => CampaignRedirection::find(),
        ]);

        return $this->render('campaigns-redirections', [
            'dataProvider' => $dataProvider,
        ]);
    }

    public function actionDisableRedirect($id){
        $model = $this->findModel($id);
        $model->redirectionStatus = 'Disabled';
        if($model->save()){
            return $this->redirect(['view', 'id' => $id]);
        }
    }

    public function actionGetActiveCampaigns($selected_campaign_id){
        Yii::$app->response->format = Response::FORMAT_JSON;
        $campaigns_models = Campaign::find()
            ->select(['_id', 'name'])
            ->where([
                'status' => 'Active'
            ])
            ->andWhere([
                '_id' => [
                    '$ne' => Utilities::getIdAsObject($selected_campaign_id)
                ]
            ])
            ->all();
    
        return [
            'status' => true,
            'data' => $campaigns_models
        ];
    } 

    public function actionTerminateCampaign($id){
        $model = $this->findModel($id);
        $model->scenario = Campaign::SCENARIO_TERMINATE;

        if($model->load(Yii::$app->request->post())){
            $collection = Yii::$app->mongodb->getCollection('campaigns');

            $data = [
                'status' => 'Terminated',
                'terminatedBy' => Yii::$app->user->identity->_id,
                'terminatedAt' => Utilities::getDateTime(),
                'terminationReason' => $model->terminationReason
            ];

            $collection->update([
                '_id' => Utilities::getIdAsObject($id),
            ], $data);

            return $this->redirect(['view', 'id' => (string)$model->_id]);
        }

        return $this->renderAjax('terminate-campaign-modal', [
            'model' => $model,
        ]);
    }

    // HERE ARE THE MODIFICATIONS

    public function actionPublicForm($campaign_id){
        $this->layout = 'campaign-form-main';

        $campaign = $this->findModel($campaign_id);
        $form_id = $campaign->formId;

        // Check form status (if it's inactive, the campaign mustn't be rendered)
        if(! Utilities::formIsActive($form_id)){
            return $this->render('/campaigns/unavailable-campaign', [
                'message' => 'This campaign can not be shown at this time because the related form has been deactivated',
            ]);
        }

        if($campaign->status == 'Terminated'){
            if(! $campaign->mustRedirectTo){
                return $this->render('/campaigns/unavailable-campaign', [
                    'message' => 'This campaign has been terminated',
                ]);
            }
            else{
                // We have to redirect user to the destination campaign if any
                if($campaign->redirectionStatus == 'Enabled'){
                    $keep_redirect = true;
                    $current_campaign = $campaign;
                    $redirection_link = $current_campaign->mustRedirectTo;
                    while($keep_redirect){
                        $redirect_to_campaign_id = explode('campaign_id=', $redirection_link)[1];
                        $old_campaign = $current_campaign;
                        $current_campaign = Campaign::findOne($redirect_to_campaign_id);
                        if($current_campaign){
                            if($current_campaign->mustRedirectTo == null){
                                $keep_redirect = false;
                                $redirection_link = $old_campaign->mustRedirectTo;
                            }
                            elseif($current_campaign->redirectionStatus == 'Disabled'){
                                $keep_redirect = false;
                                $redirection_link = null;
                            }
                        }
                    }

                    if($redirection_link){
                        return $this->redirect(['public-form', 'campaign_id' => explode('campaign_id=', $redirection_link)[1]]);
                    }
                }
                return $this->render('/campaigns/unavailable-campaign', [
                    'message' => 'This campaign has been terminated',
                ]);
            }
        }
        if($campaign->status == 'Paused'){
            return $this->render('/campaigns/unavailable-campaign', [
                'message' => 'This campaign has been paused',
            ]);
        }
        if(strtotime($campaign->startDate) > time()){
            return $this->render('/campaigns/unavailable-campaign', [
                'message' => 'This campaign hasn\'t started yet',
            ]);
        }
        if(strtotime($campaign->endDate) < time()){
            return $this->render('/campaigns/unavailable-campaign', [
                'message' => 'This campaign has completed without extending its end date',
            ]);
        }
        
        $fields = $campaign->campaignFields;

        // Get the details of needed fields
        $res = (new Query())
            ->from('formsVersions')
            ->where([
                'formId' => Utilities::getIdAsObject($form_id), 
                'versionNumber' => $campaign->formVersion,
            ])
            ->one();

        if($res){
            $needed_fields = [];
            foreach($res['fields'] as $field){
                if(in_array($field['name'], $fields)){
                    $needed_fields[] = $field;
                }
            }

            $all_fields = FormHelpers::prepareFields($form_id, $needed_fields, $campaign->formVersion);
            $form_fields = $all_fields['form_fields'];
            $other_fields = $all_fields['other_fields'];
            $model = $form_fields[0]::$model;
            
            if (Yii::$app->request->isAjax) {
                $model->load(Yii::$app->request->post());
                Yii::$app->response->format = Response::FORMAT_JSON;
                return \yii\widgets\ActiveForm::validate($model);
            }

            if(Yii::$app->request->isPost){
                $data = Yii::$app->request->post();
                $model->load($data);

                $saved_files_IDs = Utilities::handleUploadedFilesInSubmission((string)$form_id, $form_fields, $model, $data);

                if($model->validate()){
                    
                    $dynamic_model_data = null;
                    $matrix_data = [];
                    FormHelpers::extractSubmittedData($data, $dynamic_model_data, $matrix_data);

                    $submitted_data = [];
                    FormHelpers::handleSubmittedFields($dynamic_model_data, $submitted_data, $form_id, $campaign->formVersion);

                    $temp_matrix_data = [];
                    FormHelpers::handleSubmittedMatrixFields($matrix_data, $temp_matrix_data);

                    FormHelpers::handleIDsFields($form_id, $submitted_data);
                    
                    $saved_document_id = $this->saveCampaignSubmittedData($campaign_id, $submitted_data);
                    if($saved_document_id){
                        Utilities::addDocumentIdToSavedFiles($saved_files_IDs, $saved_document_id);

                        foreach($temp_matrix_data as $matrix_name => $item){
                            $this->saveCampaignSubmittedMatrixData($campaign_id, $campaign->destinationNodeId, $saved_document_id, $matrix_name, $item);
                        }
                    }
                    $save_and_new = 'save-and-new';
                    if(Yii::$app->user->isGuest){
                        return $this->redirect(['public-form', 'campaign_id' => $campaign_id]);
                    }
                    if(isset($data[$save_and_new])){
                        return $this->redirect(['public-form', 'campaign_id' => $campaign_id]);
                    }
                    return $this->redirect(['/forms/view-record', 'form_id' => (string)$form_id, 'document_id' => (string)$saved_document_id]);
                }
            }

            return $this->render('render-public-form', [
                'form_fields'       => $form_fields,
                'other_fields'      => $other_fields,
                'direction'         => Utilities::getFormDirectionality($form_id),
                'form_id'           => $form_id,
            ]);
        }
    }

    public function saveCampaignSubmittedData($campaign_id, $data, $complete_flag = false, $document_id = null, $is_completed = false){
        $campaign = Campaign::findOne(Utilities::getIdAsObject($campaign_id));
        if($campaign){
            $form_id = $campaign->formId;
            $collection = Yii::$app->mongodb->getCollection('forms_' . $form_id);
            $data_item = [];
            foreach($data as $item){
                if(! Utilities::isMatrix($item['name'])){
                    $data_item[$item['name']] = $item['value'];
                }
            }

            if($complete_flag){
                if($is_completed){
                    $data_item['completedBy'] = Yii::$app->user->identity->_id;
                    $data_item['completedAt'] = Utilities::getDateTime();
                    $data_item['isCompleted'] = true;
                }
                
                $result = $collection->update([
                    '_id' => Utilities::getIdAsObject($document_id)
                ], [
                    '$set' => $data_item
                ]);
    
                // If we return $result, it might be 0 if the user hasn't change any value, so we return document_id
                return $document_id;
            }

            // Check if there is at least one field in data-completion-fields
            $is_completed = count($campaign->dataCompletionFields) == 0;

            $data_item['submittedBy'] = ! Yii::$app->user->isGuest ? Yii::$app->user->identity->_id : null;
            $data_item['submittedAt'] = Utilities::getDateTime();
            $data_item['ownerNodeId'] = $campaign->destinationNodeId;
            $data_item['formVersion'] = $campaign->formVersion;
            $data_item['campaignId']  = Utilities::getIdAsObject($campaign_id);
            $data_item['isCompleted'] = $is_completed;
            $result = $collection->insert($data_item);

            if($result){
                // dataNodesCollections is used to facilitate searching of data in collections and owner node
                Utilities::addRecordToDataNodesCollections($data_item['ownerNodeId'], $result, 'forms_' . $form_id);
            }
            return $result;
        }
    }

    public function saveCampaignSubmittedMatrixData($campaign_id, $destination_node_id, $parent_document_id, $matrix_name, $data, $completion = false){
        $collection = Yii::$app->mongodb->getCollection($matrix_name);

        if($completion){
            // Delete all previous documents
            $collection->remove(['id' => Utilities::getIdAsObject($parent_document_id)]);
        }

        foreach($data as $record){
            $data_item = [];
            $data_item['id'] = Utilities::getIdAsObject($parent_document_id);
            foreach($record as $key => $value){
                $data_item[$key] = $value;
            }

            $data_item['submittedBy'] = ! Yii::$app->user->isGuest ? Yii::$app->user->identity->_id : null;
            $data_item['submittedAt'] = Utilities::getDateTime();
            $data_item['ownerNodeId'] = $destination_node_id;
            $data_item['campaignId']  = Utilities::getIdAsObject($campaign_id);

            $result = $collection->insert($data_item);

            if($result){
                // dataNodesCollections is used to facilitate searching of data in collections and owner node
                Utilities::addRecordToDataNodesCollections($data_item['ownerNodeId'], $result, $matrix_name);
            }
        }
    }


    public function test(){

    }

    // Completing record will render the same form of updating record, but we will exclude unneeded fields
    // Unneeded fields are the fields those not found in campaign-fields or data-completion-fields
    // The campaign-fields will be rendered as readonly fields
    public function actionCompleteRecord($campaign_id, $document_id){
        $campaign = $this->findModel($campaign_id);
        $form_id = (string)$campaign->formId;
        
        if(! Yii::$app->user->can('Admin')){
            if(! Utilities::checkPermissionForForm($form_id, 'complete-data')){
                return $this->redirect(['index']);
            }
        }

        $document = Utilities::getSpecificDocument($form_id, $document_id);

        if($document){
            // This checks the record accessibility according to node
            // (The user can access form submissions if he/she has a read permission, but if he/she tries to access specific record by typing its id in url, we have to check if he/she can view this record according to nodes tree)
            $nodes = Utilities::getNodeAndItsChildrenIDs();
            if(! in_array($document['ownerNodeId'], $nodes)){
                return $this->redirect(['index']);
            }

            $needed_fields = [];

            $form = Utilities::getActiveVersionOfForm($form_id);
            foreach($form['fields'] as $field){
                if(in_array($field['name'], $campaign->campaignFields) || in_array($field['name'], $campaign->dataCompletionFields)){
                    $needed_fields[] = $field;
                }
            }

            $all_fields = FormHelpers::prepareFields($form_id, $needed_fields, $campaign->formVersion, $document);
            $form_fields = $all_fields['form_fields'];
            $other_fields = $all_fields['other_fields'];
            $model = $form_fields[0]::$model;

            if (Yii::$app->request->isAjax) {
                $model->load(Yii::$app->request->post());
                Yii::$app->response->format = Response::FORMAT_JSON;
                return \yii\widgets\ActiveForm::validate($model);
            }

            if(Yii::$app->request->isPost){
                $data = Yii::$app->request->post();
                
                // Exclude the campaign fields (the user may inspect fields in browser and change disabled attribute, so he/she can change the original susbmitted data)
                foreach($data['DynamicModel'] as $data_key => $data_value){
                    if(in_array($data_key, $campaign->campaignFields)){
                        unset($data['DynamicModel'][$data_key]);
                    }
                }
                
                $model->load($data);

                // Must handle files in order to pass validation
                // document_id will be generated finally, so we will keep the filesCollection documents_IDs in array in order to add document_id later
                $saved_files_IDs = Utilities::handleUploadedFilesInSubmission($form_id, $form_fields, $model, $data);

                // Here we validate only the remaining fields
                if($model->validate($campaign->dataCompletionFields)){
                    $dynamic_model_data = null;
                    $matrix_data = [];
                    FormHelpers::extractSubmittedData($data, $dynamic_model_data, $matrix_data);

                    $submitted_data = [];
                    FormHelpers::handleSubmittedFields($dynamic_model_data, $submitted_data, $form_id, $campaign->formVersion);

                    $temp_matrix_data = [];
                    FormHelpers::handleSubmittedMatrixFields($matrix_data, $temp_matrix_data);

                    $save_and_complete = 'save-and-complete';
                    $is_completed = false;
                    if(isset($data[$save_and_complete])){
                        $is_completed = true;
                    }

                    $saved_document_id = $this->saveCampaignSubmittedData($campaign_id, $submitted_data, true, $document_id, $is_completed);
                    if($saved_document_id){
                        Utilities::addDocumentIdToSavedFiles($saved_files_IDs, $saved_document_id);

                        foreach($temp_matrix_data as $matrix_name => $item){
                            $this->saveCampaignSubmittedMatrixData($campaign_id, $campaign->destinationNodeId, $saved_document_id, $matrix_name, $item, true);
                        }

                        // Re-calculate calculated fields if any (because some fields might be filled-out in completion process)
                        $calculated_fields = Utilities::getCalculatedFieldsExpressions($campaign->formId, $campaign->formVersion);
                        if($calculated_fields){
                            // Get record again after saving its fields
                            $saved_record = Utilities::getSpecificDocument($form_id, $document_id);
                            $results = [];

                            foreach($calculated_fields as $calculated_field => $expression){
                                if(preg_match_all(Utilities::getCalculatedFieldExpressionPatern(), $expression, $matches)){
                                    $matches = $matches[0];
                                    // Loop throughot fields in expression in order to replace fields names by its values
                                    foreach($matches as $match){
                                        $matched_field_name = str_replace('[', '', str_replace(']', '', $match));
                                        if(isset($saved_record[$matched_field_name])){
                                            $expression = str_replace($matched_field_name, $saved_record[$matched_field_name], $expression);
                                        }
                                    }
                                }
                                $expression = str_replace('[', '', str_replace(']', '', $expression));
                                $calculator = new \app\components\FieldCalculate();
                                $results[$calculated_field] = $calculator->calculate($expression);
                            }

                            if($results){
                                $collection = Yii::$app->mongodb->getCollection('forms_' . $form_id);
                                $collection->update([
                                    '_id' => Utilities::getIdAsObject($document_id)
                                ], [
                                    '$set' => $results
                                ]);
                            }
                        }

                        if($is_completed){
                            // Update matrix records to mark as completed
                            $matrices_names = Utilities::getMatricesNamesOfForm($form_id);
                            if($matrices_names){
                                foreach($matrices_names as $matrix_name){
                                    $collection = Yii::$app->mongodb->getCollection($matrix_name);
                                    $collection->update([
                                        'id' => Utilities::getIdAsObject($document_id)
                                    ], [
                                        '$set' => [
                                            'completedBy' => Yii::$app->user->identity->_id,
                                            'completedAt' => Utilities::getDateTime(),
                                            'isCompleted' => true,
                                        ]
                                    ], [
                                        'multi' => true
                                    ]);
                                }
                            }
                        }
    
                        return $this->redirect(['campaigns/uncompleted-data', 'campaign_id' => $campaign_id]);
                    }
                }
            }

            // Get the submitted matrix records
            $matrices_results = FormHelpers::handleMetricesForUpdateAndCompleteRecord($other_fields, $document_id);

            // Select fields must be filled-out with the appropriate data according to its parents dropdown fields (related lists)
            $list_values = FormHelpers::handleRelatedListsForUpdateAndCompleteRecord($form_id, $form_fields, $document);

            // Show if fields: We have to display the fields those are hidden according to an option
            $show_if_fields = FormHelpers::handleShowIfForUpdateAndCompleteRecord($form_fields);

            return $this->render('render-public-form', [
                'form_fields'       => $form_fields,
                'other_fields'      => $other_fields,
                'direction'         => Utilities::getFormDirectionality($form_id),
                'form_id'           => $form_id,
                'matrices_results'  => Utilities::customJsonEncode($matrices_results),
                'list_values'       => Utilities::customJsonEncode($list_values),
                'show_if_fields'    => Utilities::customJsonEncode($show_if_fields),
                'read_only_fields'  => $campaign->campaignFields,
            ]);
        }
    }

    public function actionDelete($id){
        $collection = Yii::$app->mongodb->getCollection('campaigns');
        $collection->remove(['_id' => Utilities::getIdAsObject($id)]);
        return $this->redirect(['index']);
    }
}