<?php

namespace app\controllers;

use Yii;
use yii\web\Controller;
use yii\web\Response;
use yii\mongodb\Query;
use app\components\Utilities;
use app\models\Node;

class NodesController extends Controller
{
    public function behaviors()
    {
        return [
            'access' => [
                'class' => \yii\filters\AccessControl::class,
                'rules' => [
                    [
                        'allow' => true,
                        'actions' => ['get-current-active-node'],
                        'roles' => ['User']
                    ],
                    [
                        'allow' => true,
                        'roles' => ['Admin']
                    ],
                ],
                'denyCallback' => function(){
                    if(Yii::$app->user->isGuest){
                        return Yii::$app->response->redirect(['/users/login']);
                    }
                    return Yii::$app->response->redirect(Yii::$app->homeUrl);
                }
            ]
        ];
    }

    public function actionCreate(){
        if(Yii::$app->request->isPost){
            Yii::$app->response->format = Response::FORMAT_JSON;
            $data = Yii::$app->request->post();
            $name = $data['name'];
            $parent_id = $data['parentId'];
            $model = new Node();
            $model->attributes = [
                'name' => $name,
                'parentId' => $parent_id,
                'createdAt' => date('Y-m-d G:i:s'),
            ];
            if(! $model->save()){
                return Utilities::getFalseStatusWithMessage('COULD_NOT_CREATE_NODE');
            }

            // Add created node to parent's children
            $parent_node = Node::findOne(['name' => $parent_id]);
            if(! $parent_node){
                return Utilities::getFalseStatusWithMessage('COULD_NOT_FIND_PARENT_NODE');
            }

            $result = Yii::$app->mongodb->getCollection('nodes')->update([
                '_id' => $parent_node['_id']
            ], [
                '$push' => [
                    'children' => $name 
                ]
            ]);
            if(! $result){
                return Utilities::getFalseStatusWithMessage('COULD_NOT_ADD_NODE_TO_PARENT_CHILDREN');
            }
            
            return [
                'status' => true,
                'data' => [
                    'id' => (string)$model->_id,
                    'name' => $name
                ]
            ];
        }
    }

    public function actionRename(){
        if(Yii::$app->request->isPost){
            Yii::$app->response->format = Response::FORMAT_JSON;
            $data = Yii::$app->request->post();
            $old_name = $data['oldName'];
            $new_name = $data['newName'];
            $parent_id = $data['parentId'];
            // Check if the new name already exists
            $_node = Node::findOne(['name' => $new_name]);
            if($_node){
                return Utilities::getFalseStatusWithMessage('THE_NAME_ALREADY_EXISTS');
            }

            $collection = Yii::$app->mongodb->getCollection('nodes');
            $result = $collection->update([
                'name' => $old_name
            ], [
                '$set' => [
                    'name' => $new_name
                ]
            ]);
            if(! $result){
                return Utilities::getFalseStatusWithMessage('COULD_NOT_UPDATE_NODE');
            }

            // Update parent children array
            $parent_result = $collection->update([
                'name' => $parent_id,
                'children' => $old_name
            ], [
                '$set' => [
                    'children.$' => $new_name
                ]
            ]);
            if(! $parent_result){
                return Utilities::getFalseStatusWithMessage('COULD_NOT_UPDATE_PARENT_NODE');
            }

            return [
                'status' => true,
                'data' => [
                    'name' => $new_name
                ]
            ];
        }
    }

    // The node mustn't be deleted if it or its children have data or assigned users
    public function actionCheckIfCanDelete(){
        if(Yii::$app->request->isPost){
            Yii::$app->response->format = Response::FORMAT_JSON;
            $data = Yii::$app->request->post();
            $node_id = $data['id'];
            $parent_id = $data['parentId'];
            $node = Node::findOne(['name' => $node_id]);
            if(! $node){
                return Utilities::getFalseStatusWithMessage('COULD_NOT_FIND_NODE');
            }

            // Check if there is data in the node or in its children 
            $node_children = Utilities::getSpecificNodeAndItsChildrenIDs($node_id);
            $data_exists_in_node = (new Query())
                ->from('dataNodesCollections')
                ->where([
                    'nodeId' => [
                        '$in' => $node_children
                    ]
                ])
                ->one();
            
            if($data_exists_in_node){
                return Utilities::getFalseStatusWithMessage('COULD_NOT_DELETE_NODE_BECAUSE_DATA_INTEGRITY');
            }

            // Check if there are members in the node
            $user_exists_in_node = (new Query())
                ->from('users')
                ->where([
                    'nodes' => (string)$node['_id'] 
                ])
                ->one();

            if($user_exists_in_node){
                return Utilities::getFalseStatusWithMessage('COULD_NOT_DELETE_NODE_BECAUSE_USERS_MEMBERSHIP');
            }

            return [
                'status' => true,
                'data' => [
                    'id' => $node_id,
                    'parent_id' => $parent_id
                ]
            ];
        }
    }

    public function actionDelete(){
        if(Yii::$app->request->isPost){
            Yii::$app->response->format = Response::FORMAT_JSON;
            $data = Yii::$app->request->post();
            $node_id = $data['id'];
            $parent_id = $data['parentId'];
            $collection = Yii::$app->mongodb->getCollection('nodes');
            $result = $collection->remove([
                'name' => $node_id
            ]);
            if(! $result){
                return Utilities::getFalseStatusWithMessage('COULD_NOT_DELETE_NODE');
            }

            // Remove the deleted node name from parents's children
            $parent_node = Node::findOne(['name' => $parent_id]);
            if(! $parent_node){
                return Utilities::getFalseStatusWithMessage('COULD_NOT_FIND_PARENT_NODE');
            }

            $parent_result = $collection->update([
                'name' => $parent_id,
            ], [
                '$pull' => [
                    'children' => $node_id
                ]
            ]);
            if(! $parent_result){
                return Utilities::getFalseStatusWithMessage('COULD_NOT_UPDATE_PARENT_NODE');
            }
            
            return [
                'status' => true,
                'data' => true
            ];
        }
    }

    public function actionMove(){
        if(Yii::$app->request->isPost){
            Yii::$app->response->format = Response::FORMAT_JSON;
            $data = Yii::$app->request->post();
            $node_id = $data['id'];
            $old_parent_id = $data['oldParentId'];
            $new_parent_id = $data['newParentId'];

            $collection = Yii::$app->mongodb->getCollection('nodes');
            $reference = Node::findOne(['name' => $node_id]);
            if(! $reference){
                return Utilities::getFalseStatusWithMessage('COULD_NOT_FIND_REFERENCE_NODE');
            }

            // Check parent for node and reference node
            if($old_parent_id == $new_parent_id){
                // Just re-arrange in the same level
                $parent_children = $data['oldParentChildren'];
                $parent_result = $collection->update([
                    'name' => $old_parent_id,
                ], [
                    '$set' => [
                        'children' => $parent_children
                    ]
                ]);
                if(! $parent_result){
                    return Utilities::getFalseStatusWithMessage('COULD_NOT_UPDATE_PARENT_NODE');
                }

                return [
                    'status' => true,
                    'data' => true
                ];
            }
            
            // Update reference node's parent
            $node_result = $collection->update([
                'name' => $node_id,
            ], [
                '$set' => [
                    'parentId' => $new_parent_id
                ]
            ]);
            if(! $node_result){
                return Utilities::getFalseStatusWithMessage('COULD_NOT_UPDATE_REFERENCE_NODE');
            }

            // Update node's old parent's children
            $old_parent_children = $data['oldParentChildren'] ?? null;
            $old_parent_result = false;
            if($old_parent_children){
                $old_parent_result = $collection->update([
                    'name' => $old_parent_id,
                ], [
                    '$set' => [
                        'children' => $old_parent_children
                    ]
                ]);
            }
            else{
                $old_parent_result = $collection->update([
                    'name' => $old_parent_id,
                ], [
                    '$unset' => [
                        'children' => ''
                    ]
                ]);
            }
            
            if(! $old_parent_result){
                return Utilities::getFalseStatusWithMessage('COULD_NOT_UPDATE_OLD_PARENT_NODE');
            }

            // Update node's new parent's children
            $new_parent_children = $data['newParentChildren'] ?? null;
            $new_parent_result = false;
            if($new_parent_children){
                $new_parent_result = $collection->update([
                    'name' => $new_parent_id,
                ], [
                    '$set' => [
                        'children' => $new_parent_children
                    ]
                ]);
            }
            else{
                $new_parent_result = $collection->update([
                    'name' => $new_parent_id,
                ], [
                    '$unset' => [
                        'children' => ''
                    ]
                ]);
            }
            
            if(! $new_parent_result){
                return Utilities::getFalseStatusWithMessage('COULD_NOT_UPDATE_NEW_PARENT_NODE');    
            }

            return [
                'status' => true,
                'data' => true
            ];
        }
    }

    public function actionTree($modal = false){
        $data = [];
        $root = Node::findOne(['name' => 'Root']);
        
        if($root){
            $data = [
                'id' => 'Root',
                'text' => 'Root',
            ];
            if(isset($root['children'])){
                foreach($root['children'] as $child){
                    $node = Node::findOne(['name' => $child]);
                    if($node){
                        if(isset($node['children'])){
                            $data['children'][] = $this->handleNode($node);
                        }
                        else{
                            $data['children'][] = [
                                'id' => $child,
                                'text' => $child
                            ];
                        }
                    }
                }
            }
        }

        if(! $modal){
            return $this->render('tree', [
                'data' => Utilities::customJsonEncode($data)
            ]);
        }
        return Utilities::customJsonEncode($data);
    }

    public function handleNode($node){
        $item = [
            'id' => $node['name'],
            'text' => $node['name'],
        ];
        foreach($node['children'] as $child){
            $node = Node::findOne(['name' => $child]);
            if($node){
                if(isset($node['children'])){
                    $item['children'][] = $this->handleNode($node);
                }
                else{
                    $item['children'][] = [
                        'id' => $child,
                        'text' => $child
                    ];
                }
            }
        }

        return $item;
    }

    public function actionShowNodePathModal($node_id){
        $path = '';
        $node = Node::findOne($node_id);
        if($node){
            $parents = [];
            $parents[] = $node['name'];
            $parent_node_name = '';
            while($parent_node_name != 'Root'){
                $parent_node = Node::find()
                    ->where(['IN', 'children', $node['name']])
                    ->one();
                if($parent_node){
                    $parent_node_name = $parent_node['name'];
                    $parents[] = $parent_node_name;
                    $node = $parent_node;
                }
            }
            $path = array_reverse($parents);
        }
        return $this->renderAjax('node-path-modal', [
            'path' => $path,
        ]);
    }

    public function actionShowTreeModal(){
        return $this->renderAjax('tree-modal', [
            'data' => $this->actionTree(true)
        ]);
    }

    public function actionShowTreeModalForAdmin(){
        return $this->renderAjax('tree-modal-admin', [
            'data' => $this->actionTree(true)
        ]);
    }

    public function actionShowTreeModalForCampaign(){
        return $this->renderAjax('tree-modal-campaign', [
            'data' => $this->actionTree(true)
        ]);
    }

    public function actionGetCurrentActiveNode(){
        return Yii::$app->session->get('user.active_node');
    }
}