<?php

require_once APPPATH . 'libraries/Cf_REST_Controller.php';

class Astrolabe extends Cf_REST_Controller
{
    public function __construct()
    {
        parent::__construct();
        $this->load->model('astrolabe_model');
        $this->load->model('host_model');
        $this->username = $this->session->userdata('username');
        $this->load->driver('cache', ['adapter' => 'file']);
    }

    public function host_post()
    {
        $page = isset($_REQUEST['page']) ? $_REQUEST['page'] : 1;
        //If the count doesn't set, set it to 999999 (as no limit)
        $count = isset($_REQUEST['count']) ? $_REQUEST['count'] : UNLIMITED_COUNT;
        $hosts = $this->host_model->getHostListByContext(
            $this->param_includes(),
            $this->param_excludes(),
            false,
            $page,
            $count,
        );
        $this->respond(200, json_encode($hosts));
    }

    public function profile_get($id = null, $shared = null, $sharedFrom = null)
    {
        if (is_null($id)) {
            $roles = $this->session->userdata('roles') ? $this->session->userdata('roles') : [];
            $profileList = $this->astrolabe_model->profile_list($this->username, $roles);
            $this->respond(200, json_encode($profileList));
        } else {
            $roles = $this->session->userdata('roles') ? $this->session->userdata('roles') : [];
            $profile = $this->astrolabe_model->profile_get(
                $this->username,
                $id,
                $shared,
                $sharedFrom,
                '{' . implode(',', $roles) . '}',
            );
            if ($profile !== null) {
                $this->respond(200, json_encode($profile->data));
            } else {
                $this->respond(404);
            }
        }
    }

    public function profile_put($profileName)
    {
        $profileName = urldecode($profileName);

        if (!$this->isValidName($profileName)) {
            $this->respond_not_acceptable();
            return;
        }

        $data = json_decode($this->_put_args, true);
        $node_description_list = $data['nodeDescription'];
        if ($this->__is_valid_node_description_list($node_description_list)) {
            $sharedPermission = !empty($data['sharedPermission']) ? $data['sharedPermission'] : [];
            $global = !empty($data['global']) ? $data['global'] : false;
            $meta = ['sharedPermission' => $sharedPermission, 'globaltree' => $global];
            $id = $this->astrolabe_model->profile_insert($this->username, $profileName, $node_description_list, $meta);
            $responseData = ['id' => $id];
            $this->respond_ok(json_encode($responseData));
        } else {
            $this->respond_not_acceptable();
        }
    }

    public function profile_post($id)
    {
        $newName = $this->param('newname', true);
        $nodeData = $this->param('nodeData', true);
        $global = $this->param('globalTree', true);
        $sharedPermission = $this->param('sharedPermission', true);

        $updateData = [];
        if (!empty($newName)) {
            if (!$this->isValidName($newName)) {
                $this->respond_not_acceptable();
                return;
            }
            $updateData['profileId'] = $newName;
        }
        if (!empty($nodeData)) {
            $updateData['data'] = json_decode($nodeData, true);
        }
        if ($global !== false) {
            $updateData['globaltree'] = (bool) $global;
        }
        if ($sharedPermission) {
            $updateData['sharedPermission'] = $sharedPermission;
        }

        $result = $this->astrolabe_model->profile_update($this->username, $id, $updateData);

        if (is_array($result) && !empty($result['err'])) {
            $this->respond(500, 'Error updating tree name');
        }

        $this->respond_ok();
    }

    public function profile_delete($id)
    {
        if ($this->astrolabe_model->profile_delete($this->username, $id) == true) {
            $this->respond(204);
        } else {
            $this->respond(500, 'Error deleting profile');
        }
    }

    public function subscribe_put($id)
    {
        $roles = $this->session->userdata('roles') ? $this->session->userdata('roles') : [];
        if ($this->astrolabe_model->subscription_put($this->username, $roles, $id)) {
            $this->respond(200);
        } else {
            $this->respond(500, 'Error subscribing.');
        }
    }

    public function subscribe_delete($id)
    {
        if ($this->astrolabe_model->subscription_delete($this->username, $id)) {
            $this->respond(200);
        } else {
            $this->respond(500, 'Error subscribing.');
        }
    }

    public function copy_put($id, $from)
    {
        $result = $this->astrolabe_model->copy_tree($this->username, $from, $id);
        if ($result) {
            $this->respond(200, json_encode($result));
        } else {
            $this->respond(500, 'Error copying tree.');
        }
    }

    private function isValidName($name)
    {
        $pattern = '/^[a-zA-Z0-9 _-]{1,100}$/';
        return preg_match($pattern, $name) === 1;
    }

    public function __is_valid_node_description_list($node_description_list)
    {
        if (!is_array($node_description_list)) {
            return false;
        }

        foreach ($node_description_list as $child) {
            if (!$this->__is_valid_node_description($child)) {
                return false;
            }
        }

        return true;
    }

    public function __is_valid_node_description($node_description)
    {
        if (!array_key_exists('label', $node_description) || !$node_description['label']) {
            return false;
        }

        $label = $node_description['label'];
        if (!$this->isValidName($label)) {
            return false;
        }

        if (!array_key_exists('classRegex', $node_description) || !$node_description['classRegex']) {
            return false;
        }

        if (array_key_exists('children', $node_description)) {
            if (!$this->__is_valid_node_description_list($node_description['children'])) {
                return false;
            }
        }

        return true;
    }
}
