<?php
require_once APPPATH . 'libraries/Cf_REST_Controller.php';

class Projects extends Cf_REST_Controller
{
    public const DEFAULT_LOCAL_DEPLOY_PATH = '/opt/cfengine/build/local_deploy';
    public const DEFAULT_LOCAL_DEPLOY_BRANCH = 'main';

    public function __construct()
    {
        parent::__construct();
        $this->load->model('build_app/projects_model');
    }

    public function project_post()
    {
        try {
            $data = json_decode($this->request->body, true);
            $result = $this->projects_model->create($data);
            $this->respond(200, $result);
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to create project. Error: ' . $exception->getMessage());
            throw $exception;
        }
    }

    public function project_put($id)
    {
        try {
            $data = json_decode($this->_put_args, true);
            $data['is_local'] = false;
            $result = $this->projects_model->update($id, $data);
            $this->respond(200, $result);
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to update project. Error: ' . $exception->getMessage());
            throw $exception;
        }
    }

    public function project_delete($id)
    {
        try {
            $result = $this->projects_model->remove($id);
            $this->respond(200, $result);
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to delete project. Error: ' . $exception->getMessage());
            throw $exception;
        }
    }

    public function push_post($id)
    {
        try {
            $result = $this->projects_model->push($id);
        } catch (Exception $exception) {
            log_message('error', 'Unable to push project to remote git repository. Error: ' . $exception->getMessage());
            throw $exception;
        }
        $this->respond(200, $result);
    }

    public function localDeploy_post($id)
    {
        try {
            $result = $this->projects_model->localDeploy($id);
        } catch (Exception $exception) {
            log_message('error', 'Unable to deploy project locally. Error: ' . $exception->getMessage());
            throw $exception;
        }
        $this->respond(200, $result);
    }

    public function synchronize_post($id)
    {
        try {
            $localDeploy = isset($_GET['localDeploy']) && $_GET['localDeploy'] === 'true' ? 'true' : 'false';
            $result = $this->projects_model->synchronize($id, $localDeploy);
            $this->respond(200, $result);
            return;
        } catch (Exception $exception) {
            log_message(
                'error',
                'Unable to set up deployment of this project by updating VCS settings. Error: ' .
                    $exception->getMessage(),
            );
            throw $exception;
        }
    }

    public function local_project_post()
    {
        try {
            $data = json_decode($this->request->body, true);
            $result = $this->projects_model->createLocal($data);
            $this->respond(200, $result);
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to create empty project. Error: ' . $exception->getMessage());
            throw $exception;
        }
    }

    public function project_get($id)
    {
        try {
            $project = $this->projects_model->get($id);
            $this->respond(200, $project);
        } catch (HttpClient_NotFound $exception) {
            $this->respond(404, $exception->getMessage());
        } catch (Exception $exception) {
            log_message('error', 'Unable to get project. Error: ' . $exception->getMessage());
            throw $exception;
        }
    }

    public function list_get()
    {
        try {
            $projects = $this->projects_model->list($_GET);
        } catch (Exception $exception) {
            log_message('error', 'Unable to get projects list. Error: ' . $exception->getMessage());
            throw $exception;
        }
        $this->respond(200, $projects);
    }

    public function modules_get($id)
    {
        try {
            $result = $this->projects_model->getModules($id);
            $this->respond(200, $result);
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to get project modules. Error: ' . $exception->getMessage());
            throw $exception;
        }
    }

    public function vcsSynchronized_get($id)
    {
        try {
            $result = $this->projects_model->vcsSynchronized($id);
            $this->respond(200, $result);
            return;
        } catch (Exception $exception) {
            log_message(
                'error',
                'Unable to check if a project synchronised with VCS settings. Error: ' . $exception->getMessage(),
            );
            throw $exception;
        }
    }

    // If VCS settings have already set up with git cfbs type then some project was already deployed
    public function anyProjectDeployed_get()
    {
        $this->load->model('vcsSettingsModel');
        try {
            $settings = $this->vcsSettingsModel->getSettings();
            $response = ['deployed' => $settings['vcsType'] == VcsSettingsModel::GIT_CFBS_VCS_TYPE];
        } catch (HttpClient_NotFound $exception) {
            // api returns 404 response when no config found, this means no projects deployed
            $response = ['deployed' => false];
        }
        $this->respond(200, json_encode($response));
    }

    public function module_post($projectId, $name, $version)
    {
        try {
            $result = $this->projects_model->addModule($projectId, $name, $version);
            $this->respond(200, $result);
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to add module. Error: ' . $exception->getMessage());
            $this->respond(500, $exception->getMessage());
        }
    }

    public function module_put($projectId, $name, $version)
    {
        try {
            $data = json_decode($this->_put_args, true);
            $result = $this->projects_model->updateModule(
                $projectId,
                $name,
                $version,
                $_GET['encodedName'],
                $data['gitMessage'] ?? null,
            );
            $this->respond(200, $result);
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to update module. Error: ' . $exception->getMessage());
            $this->respond(500, $exception->getMessage());
        }
    }

    public function module_delete($projectId, $name)
    {
        try {
            $result = $this->projects_model->removeModule($projectId, $name, $_GET['encodedName']);
            $this->respond(200, $result);
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to delete module. Error: ' . $exception->getMessage());
            $this->respond(500, $exception->getMessage());
        }
    }

    public function module_input_get($projectId, $name)
    {
        try {
            $result = $this->projects_model->getModuleInput($projectId, $name, $_GET['encodedName']);
            $this->respond(200, $result);
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to get module input. Error: ' . $exception->getMessage());
            $this->respond(500, $exception->getMessage());
        }
    }

    public function module_input_post($projectId, $name)
    {
        try {
            $post = json_decode($this->request->body, true);
            if (!isset($post['input'])) {
                $this->respond(409, 'Missing input data');
            }
            $result = $this->projects_model->setModuleInput($projectId, $name, $post['input'], $_GET['encodedName']);
            $this->respond(200, $result);
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to set module input. Error: ' . $exception->getMessage());
            $this->respond(500, $exception->getMessage());
        }
    }

    public function module_input_render_post($projectId, $name)
    {
        try {
            $post = json_decode($this->request->body, true);
            if (!isset($post['input'])) {
                $this->respond(409, 'Missing input data');
            }
            $result = $this->projects_model->moduleInputRender($projectId, $name, $post['input']);
            $this->respond(200, $result);
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to get module input. Error: ' . $exception->getMessage());
            $this->respond(500, $exception->getMessage());
        }
    }

    public function module_by_url_post($projectId)
    {
        try {
            $post = json_decode($this->request->body, true);
            if (!isset($post['url'])) {
                $this->respond(409, 'Missing module URL');
            }
            $result = $this->projects_model->addModuleByUrl($projectId, $post['url']);
            $this->respond(200, $result);
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to add module. Error: ' . $exception->getMessage());
            $this->respond(500, $exception->getMessage());
        }
    }

    public function refresh_post($id)
    {
        try {
            $project = json_decode($this->projects_model->get($id));

            // in case of local project we look at the is_deployed_locally flag instead of fetching from the upstream
            if ($project->is_local === true) {
                $result = json_encode(['status' => $project->is_deployed_locally === true ? 'ok' : 'ahead']);
            } else {
                $result = $this->projects_model->refresh($id);
            }

            $this->respond(200, $result);
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to refresh project. Error: ' . $exception->getMessage());
            throw $exception;
        }
    }

    public function forcePull_post($id)
    {
        try {
            $result = $this->projects_model->forcePull($id);
            $this->respond(200, $result);
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to force pull project. Error: ' . $exception->getMessage());
            throw $exception;
        }
    }

    public function rebase_post($id)
    {
        try {
            $result = $this->projects_model->rebase($id);
            $this->respond(200, $result);
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to rebase project. Error: ' . $exception->getMessage());
            throw $exception;
        }
    }

    public function forceRebase_post($id)
    {
        try {
            $result = $this->projects_model->forceRebase($id);
            $this->respond(200, $result);
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to force rebase project. Error: ' . $exception->getMessage());
            throw $exception;
        }
    }

    public function defaultAction_post($id)
    {
        try {
            $post = json_decode($this->request->body, true);
            $result = $this->projects_model->defaultAction($id, $post['action']);
            $this->respond(200, $result);
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to set default action. Error: ' . $exception->getMessage());
            throw $exception;
        }
    }
}
