<?php
use Dotenv\Exception\ValidationException;

class ScopesController extends BaseController
{
    public function list()
    {
        $this->checkScopes(['admin.view']);

        $getParams = $this->request->get();

        $getParams['limit'] = $this->getDefaultLimit($getParams);
        $getParams['offset'] = array_key_exists('offset', $getParams) ? $getParams['offset'] : 0;
        $getParams['keyword'] = array_key_exists('keyword', $getParams) ? $getParams['keyword'] : '';
        $getParams['order_by'] = $getParams['order_by'] ?? 'id';
        $getParams['order_direction'] = $getParams['order_direction'] ?? 'asc';

        $params = $this->validate(
            $getParams,
            [
                'limit' => ['validators' => ['digit']],
                'offset' => ['validators' => ['digit']],
                'keyword' => ['validators' => []],
                'order_by' => ['order_by' => []],
                'order_direction' => ['order_direction' => []]
            ]
        );

        $aksi_list = Scopes::find([
            'conditions' => 'name ilike :keyword:',
            'bind' => [
                'keyword' => '%' . $params['keyword'] . '%'
            ],
            'limit' => $params['limit'],
            'offset' => $params['offset'],
            'order' => $params['order_by'] . ' ' . $params['order_direction']
        ]);

        $total_record = Scopes::count([
            'conditions' => 'name ilike :keyword:',
            'bind' => [
                'keyword' => '%' . $params['keyword'] . '%'
            ]
        ]);

        return new PaginatedResponse($aksi_list, $total_record, $params['limit'], $params['offset']);
    }

    public function create()
    {
        $this->checkScopes(['admin.view']);

        $rawBody = $this->request->getJsonRawBody(true);

        $params = $this->validate(
            $rawBody,
            [
                'name' => ['validator' => ['required']],
                'label' => ['validators' => ['required']],
                'description' => ['validators' => []]
            ]
        );

        $scopes = new Scopes();
        $scopes->name = $params['name'];
        $scopes->label = $params['label'];
        $scopes->description = $params['description'];
        $scopes->created_at = date('Y-m-d H:i:s');
        $status = $scopes->save();

        if (!$status) {
            $errors = $scopes->getMessage();
            $error_messages = [];

            foreach ($errors as $key => $err) {
                $error_messages[] = ["field" => $err->getField(), "message" => $err->getMessage()];
            }

            throw new ValidationException($error_messages);
        } else {
            return new CollectionPaginatedResponse([$scopes]);
        }
    }

    public function update($id)
    {
        $this->checkScopes(['admin.view']);

        $scopes = Scopes::findFirst($id);

        if (empty($scopes)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Scopes tidak diketemukan');
        }
        if (!empty($scopes->deleted_at)) {
            throw new CustomErrorException(
                BaseResponse::INVALID_REQUEST,
                'Scopes mungkin sudah terhapus'
            );
        }

        $rawBody = $this->request->getJsonRawBody(true);

        $params = $this->validate(
            $rawBody,
            [
                'name' => ['validators' => ['required']],
                'label' => ['validators' => ['required']],
                'description' => ['validators' => []]
            ]
        );

        $scopes->name = $params['name'];
        $scopes->label = $params['label'];
        $scopes->description = $params['description'];
        $scopes->updated_at = date('Y-m-d H:i:s');
        $status = $scopes->save();

        if (!$status) {
            $errors = $scopes->getMessage();
            $error_messages = [];

            foreach ($errors as $key => $error) :
                $error_messages[] = ['field' => $error->getField(), 'message' => $error->getMessage()];
            endforeach;

            throw new ValidationException($error_messages);
        } else {
            return new CollectionPaginatedResponse([$scopes]);
        }
    }

    public function detail($id)
    {
        $this->checkScopes(['admin.view']);

        $scopes = Scopes::findFirst($id);

        if (empty($scopes)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Scopes tidak diketemukan');
        }

        $result = $scopes->toDetail();

        return new CollectionPaginatedResponse([$result]);
    }

    public function remove($id)
    {
        $this->checkScopes(['admin.view']);

        $scopes = Scopes::findFirst($id);
        if (empty($scopes)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Scopes tidak diketemukan atau mungkin sudah terhapus');
        }

        if (!empty($scopes->deleted_at)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Scopes mungkin sudah terhapus');
        }

        $date = date('Y-m-d H:i:s');
        $scopes->updated_at = $date;
        $scopes->deleted_at = $date;
        $status = $scopes->save();

        if (!$status) {
            $errors = $scopes->getMessages();
            $error_messages = [];

            foreach ($errors as $key => $error) {
                $error_messages[] = ["field" => $error->getField(), "message" => $error->getMessage()];
            }

            throw new ValidationException($error_messages);
        } else {
            return new CollectionPaginatedResponse([$scopes]);
        }
    }

    public function restore($id)
    {
        $this->checkScopes(['admin.view']);

        $scopes = Scopes::findFirst($id);

        if (empty($scopes)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Scopes tidak diketemukan');
        }

        if (empty($scopes->deleted_at)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Scopes sudah aktif');
        }


        $scopes->updated_at = date('Y-m-d H:i:s');
        $scopes->deleted_at = null;
        $status = $scopes->save();

        if (!$status) {
            $errors = $scopes->getMessages();
            $error_messages = [];

            foreach ($errors as $key => $error) {
                $error_messages[] = ["field" => $error->getField(), "message" => $error->getMessage()];
            }

            throw new ValidationException($error_messages);
        } else {
            return new CollectionPaginatedResponse([$scopes]);
        }
    }

    public function addRoles($id) {
        $this->checkScopes(['admin.view']);

        $rawBody = $this->request->getJsonRawBody(true);

        $params = $this->validate(
            $rawBody,
            [
                'role_id' => ['validator' => ['required']]
            ]
        );

        $roleIds = is_array($params['role_id']) ? $params['role_id'] : [$params['role_id']];

        $existingRolesScopes = RolesScopes::find([
            'conditions' => 'scope_id = :scope_id: AND role_id IN ({roleIds:array})',
            'bind' => [
                'roleIds' => $roleIds,
                'scope_id' => $id
            ],
        ])->toArray();

        $existingRoleIds = ArrayHelper::asKeyValue($existingRolesScopes, 'role_id');

        $result = [];

        foreach ($roleIds as $roleId) {
            if(!array_key_exists($roleId, $existingRoleIds)) {
                $rolesScopes = new RolesScopes();
                $rolesScopes->scope_id = $id;
                $rolesScopes->role_id = $roleId;
                $status = $rolesScopes->save();
                if (!$status) {
                    $errors = $rolesScopes->getMessage();
                    $error_messages = [];

                    foreach ($errors as $key => $err) {
                        $error_messages[] = ["field" => $err->getField(), "message" => $err->getMessage()];
                    }

                    throw new ValidationException($error_messages);
                } else {
                    $result = $rolesScopes;
                }
            }
        }

        return new CollectionPaginatedResponse([$result]);

    }

    public function deleteRoles($id) {
        $this->checkScopes(['admin.view']);

        $rawBody = $this->request->getJsonRawBody(true);

        $params = $this->validate(
            $rawBody,
            [
                'role_id' => ['validator' => ['required']]
            ]
        );

        $roleIds = is_array($params['role_id']) ? $params['role_id'] : [$params['role_id']];

        $result = RolesScopes::find([
            'conditions' => 'scope_id = :scope_id: AND role_id IN ({roleIds:array})',
            'bind' => [
                'roleIds' => $roleIds,
                'scope_id' => $id
            ],
        ])->delete();

        return new CollectionPaginatedResponse([$result]);

    }

    public function getListRoles($id) {
        $this->checkScopes(['admin.view']);

        $getParams = $this->request->get();

        $getParams['limit'] = $this->getDefaultLimit($getParams);
        $getParams['offset'] = array_key_exists('offset', $getParams) ? $getParams['offset'] : 0;
        $getParams['keyword'] = array_key_exists('keyword', $getParams) ? $getParams['keyword'] : '';
        $getParams['order_by'] = $getParams['order_by'] ?? 'id';
        $getParams['order_direction'] = $getParams['order_direction'] ?? 'asc';


        $params = $this->validate(
            $getParams,
            [
                'limit' => ['validators' => ['digit']],
                'offset' => ['validators' => ['digit']],
                'keyword' => ['validators' => []],
                'order_by' => ['order_by' => []],
                'order_direction' => ['order_direction' => []]
            ]
        );

        $scope = Scopes::findFirst($id);

        if (empty($scope)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Scope tidak ditemukan');
        }

        $listRoles = $scope->getRoles([
            'conditions' => 'name ilike :keyword:',
            'bind' => [
                'keyword' => '%' . $params['keyword'] . '%'
            ],
            'limit' => $params['limit'],
            'offset' => $params['offset'],
            'order' => '[Roles].'.$params['order_by'] . ' ' . $params['order_direction']
        ]);

        $total_record = $scope->getRoles([
            'conditions' => 'name ilike :keyword:',
            'bind' => [
                'keyword' => '%' . $params['keyword'] . '%'
            ],
            'columns' => 'COUNT(*) AS total',
            'group_by' => "[Roles].[id]"
        ])[0]['total'];

        return new PaginatedResponse($listRoles, $total_record, $params['limit'], $params['offset']);
    }

    public function listKorwilTeamRoles() {
        $this->checkOneOfScopes(['admin.view', 'korsupgah.monitoring', 'korsupgah.manage_user']);

        $selectQuery = $this->rawQuery("
            SELECT role_id as id
            FROM jaga.roles_scopes JOIN jaga.scopes
            ON scopes.id = roles_scopes.scope_id
            WHERE
            scopes.name ilike 'korsupgah.verify' or scopes.name ilike 'korwil.verifikator_kemdagri_bpkp'"
        );

        if (empty($selectQuery)) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Role dengan scope yang diperlukan untuk assign team tidak ditemukan");
        }

        return new CollectionPaginatedResponse($selectQuery);
    }


    public function listKorwilJabatanRoles() {
        $this->checkOneOfScopes(['admin.view', 'korsupgah.monitoring', 'korsupgah.manage_user']);

        $selectQuery = $this->rawQuery("
            SELECT role_id as id 
            FROM jaga.roles_scopes JOIN jaga.scopes
            ON scopes.id = roles_scopes.scope_id
            WHERE
            scopes.name ilike 'korwil.monitoring_kemdagri' "
        );

        if (empty($selectQuery)) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Role dengan scope yang diperlukan untuk assign team tidak ditemukan");
        }


        return new CollectionPaginatedResponse($selectQuery);

    }
} // end of controller
