<?php

class StranasSurveyController extends BaseController
{
    public function index()
    {
        $this->checkIsAuthenticated();

        $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['id_instansi'] = array_key_exists('id_instansi', $getParams) ? $getParams['id_instansi'] : '';
        $getParams['id_periode'] = array_key_exists('id_periode', $getParams) ? $getParams['id_periode'] : '';
        $getParams['order_by'] = array_key_exists('order_by', $getParams) ? strtolower($getParams['order_by']) : 'id';
        $getParams['order_direction'] = array_key_exists('order_direction', $getParams) ? strtolower($getParams['order_direction']) : 'asc';

        $allowed_order = ['id', 'id_periode', 'nama_instansi'];
        $allowed_order_direction = ['asc', 'desc'];
        $order_by_mapping = [
            'id' => 'StranasSurvey.id',
            'id_periode' => 'StranasSurvey.id_periode',
            'nama_instansi' => 'Instansi.nama'
        ];

        $params = $this->validate(
            $getParams,
            [
                'limit' => ['validators' => ['digit']],
                'offset' => ['validators' => ['digit']],
                'id_instansi' => ['validators' => ['digit']],
                'id_periode' => ['validators' => ['digit']],
                'keyword' => ['validators' => []],
                'order_by' => ['validators' =>  [[
                    'name' => 'inclusion_in',
                    'params' => $allowed_order
                ]]],
                'order_direction' => ['validators' =>  [[
                    'name' => 'inclusion_in',
                    'params' => $allowed_order_direction
                ]]]
            ]
        );

        $order_by_column = $order_by_mapping[$params['order_by']];
        $order_direction = $params['order_direction'];

        $builder = $this->modelsManager->createBuilder()->from('StranasSurvey')
            ->join('Instansi', 'StranasSurvey.id_instansi_satker = Instansi.id')
            ->limit($params['limit'])
            ->offset($params['offset'])
            ->orderBy(["$order_by_column $order_direction NULLS LAST"]);

        $countBuilder = $this->modelsManager->createBuilder()->from('StranasSurvey')
            ->columns(['total_record' => 'COUNT(*)'])
            ->join('Instansi', 'StranasSurvey.id_instansi_satker = Instansi.id')
            ->limit(1);

        if (!empty($params['id_instansi'])) {
            $builder->where('id_instansi_satker = :id_instansi:', ['id_instansi' => $params['id_instansi']]);
            $countBuilder->where('id_instansi_satker = :id_instansi:', ['id_instansi' => $params['id_instansi']]);
        }

        if (!empty($params['id_periode'])) {
            $builder->where('id_periode = :id_periode:', ['id_periode' => $params['id_periode']]);
            $countBuilder->where('id_periode = :id_periode:', ['id_periode' => $params['id_periode']]);
        }

        if (!empty($params['keyword'])) {
            $builder->where('Instansi.nama ilike :keyword:', ['keyword' => '%' . $params['keyword'] . '%'])
                ->orWhere('StranasSurvey.keterangan ilike :keyword:', ['keyword' => '%' . $params['keyword'] . '%']);
            $countBuilder->where('Instansi.nama ilike :keyword:', ['keyword' => '%' . $params['keyword'] . '%'])
                ->orWhere('StranasSurvey.keterangan ilike :keyword:', ['keyword' => '%' . $params['keyword'] . '%']);
        }

        $rows = $builder->getQuery()->execute();
        $total_record = (int)$countBuilder->getQuery()->execute()->getFirst()->total_record;

        $result = [];
        foreach ($rows as $key => $survey) {
            $result[] = $survey->overview();
        }

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

    public function list_detail()
    {
        $this->checkScopes(['stranas.monitoring']);

        $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['id_instansi'] = array_key_exists('id_instansi', $getParams) ? $getParams['id_instansi'] : '';
        $getParams['id_periode'] = array_key_exists('id_periode', $getParams) ? $getParams['id_periode'] : '';
        $getParams['order_by'] = array_key_exists('order_by', $getParams) ? strtolower($getParams['order_by']) : 'id';
        $getParams['order_direction'] = array_key_exists('order_direction', $getParams) ? strtolower($getParams['order_direction']) : 'asc';

        $allowed_order = ['id', 'id_periode', 'nama_instansi'];
        $allowed_order_direction = ['asc', 'desc'];
        $order_by_mapping = [
            'id' => 'StranasSurvey.id',
            'id_periode' => 'StranasSurvey.id_periode',
            'nama_instansi' => 'Instansi.nama'
        ];

        $params = $this->validate(
            $getParams,
            [
                'limit' => ['validators' => ['digit']],
                'offset' => ['validators' => ['digit']],
                'id_instansi' => ['validators' => ['digit']],
                'id_periode' => ['validators' => ['digit']],
                'keyword' => ['validators' => []],
                'order_by' => ['validators' =>  [[
                    'name' => 'inclusion_in',
                    'params' => $allowed_order
                ]]],
                'order_direction' => ['validators' =>  [[
                    'name' => 'inclusion_in',
                    'params' => $allowed_order_direction
                ]]]
            ]
        );

        $order_by_column = $order_by_mapping[$params['order_by']];
        $order_direction = $params['order_direction'];

        $builder = $this->modelsManager->createBuilder()->from('StranasSurvey')
            ->join('Instansi', 'StranasSurvey.id_instansi_satker = Instansi.id')
            ->limit($params['limit'])
            ->offset($params['offset'])
            ->orderBy(["$order_by_column $order_direction NULLS LAST"]);

        $countBuilder = $this->modelsManager->createBuilder()->from('StranasSurvey')
            ->columns(['total_record' => 'COUNT(*)'])
            ->join('Instansi', 'StranasSurvey.id_instansi_satker = Instansi.id')
            ->limit(1);

        if (!empty($params['id_instansi'])) {
            $builder->where('id_instansi_satker = :id_instansi:', ['id_instansi' => $params['id_instansi']]);
            $countBuilder->where('id_instansi_satker = :id_instansi:', ['id_instansi' => $params['id_instansi']]);
        }

        if (!empty($params['id_periode'])) {
            $builder->where('id_periode = :id_periode:', ['id_periode' => $params['id_periode']]);
            $countBuilder->where('id_periode = :id_periode:', ['id_periode' => $params['id_periode']]);
        }

        if (!empty($params['keyword'])) {
            $builder->where('Instansi.nama ilike :keyword:', ['keyword' => '%' . $params['keyword'] . '%'])
                ->orWhere('StranasSurvey.keterangan ilike :keyword:', ['keyword' => '%' . $params['keyword'] . '%']);
            $countBuilder->where('Instansi.nama ilike :keyword:', ['keyword' => '%' . $params['keyword'] . '%'])
                ->orWhere('StranasSurvey.keterangan ilike :keyword:', ['keyword' => '%' . $params['keyword'] . '%']);
        }

        $rows = $builder->getQuery()->execute();
        $total_record = (int)$countBuilder->getQuery()->execute()->getFirst()->total_record;

        $result = [];
        foreach ($rows as $key => $survey) {
            $result[] = $survey->getDetail();
        }

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

    public function all_instansi_overview()
    {
        $this->checkScopes(['stranas.monitoring']);

        $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'] = array_key_exists('order_by', $getParams) ? strtolower($getParams['order_by']) : 'percent_completion';
        $getParams['order_direction'] = array_key_exists('order_direction', $getParams) ? strtolower($getParams['order_direction']) : 'asc';

        $allowed_order = ['percent_completion', 'percent_verification', 'nama', 'total_question'];
        $allowed_order_direction = ['asc', 'desc'];

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

        $order_direction = $params['order_direction'];
        $order_column = $params['order_by'];

        $result = $this->rawQuery("
        select q.*,
            round((q.total_verified_question * 100.0) / NULLIF(q.total_answered_question,0), 2) as percent_verification,
            round((q.total_answered_question * 100.0) / NULLIF(q.total_question,0), 2) as percent_completion
            from (
                    select i.id,
                            i.nama,
                            (
                                select count(*)
                                from jaga.stranas_survey
                                where id_instansi_satker = i.id
                            ) total_survey,
                            (
                                select count(*)
                                from jaga.stranas_survey ss
                                    join jaga.stranas_periode p on ss.id_periode = p.id
                                    join jaga.stranas_survey_template t on ss.id_survey_template = t.id
                                    join jaga.stranas_survey_template_question tq on t.id = tq.id_survey_template
                                where id_instansi_satker = i.id
                            ) total_question,
                            (
                                select count(*)
                                from jaga.stranas_survey ss
                                    join jaga.stranas_periode p on ss.id_periode = p.id
                                    join jaga.stranas_survey_template t on ss.id_survey_template = t.id
                                    join jaga.stranas_survey_template_question tq on t.id = tq.id_survey_template
                                    join jaga.stranas_survey_answer sa on ss.id = sa.id_survey AND sa.id_question = tq.id_target
                                where id_instansi_satker = i.id
                            ) total_answered_question,
                            (
                                select count(*)
                                from jaga.stranas_survey ss
                                    join jaga.stranas_periode p on ss.id_periode = p.id
                                    join jaga.stranas_survey_template t on ss.id_survey_template = t.id
                                    join jaga.stranas_survey_template_question tq on t.id = tq.id_survey_template
                                    join jaga.stranas_survey_answer sa on ss.id = sa.id_survey AND sa.id_question = tq.id_target
                                where id_instansi_satker = i.id AND sa.status = :status_verified
                            ) total_verified_question
                        from jaga.instansi i
                        where i.nama ilike :keyword AND i.is_stranas = 1
            ) q
            order by $order_column $order_direction NULLS LAST
            limit :limit
            offset :offset;
        ", [
            'limit' => $params['limit'],
            'offset' => $params['offset'],
            'keyword' => '%' . $params['keyword'] . '%',
            'status_verified' => StranasSurveyAnswer::STATUS_VERIFIED
        ]);

        $total_record = Instansi::count([
            "nama ilike :keyword: AND is_stranas = 1",
            "bind" => [
                "keyword" => '%' . $params['keyword'] . '%'
            ]
        ]);

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


    public function instansi_overview()
    {
        $this->checkScopes(['stranas.monitoring']);

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

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

        $allowed_order = ['percent_completion', 'percent_verification', 'total_question', 'total_verified_question', 'total_answered_question'];
        $allowed_order_direction = ['asc', 'desc'];

        $params = $this->validate(
            $getParams,
            [
                'id_instansi' => ['validators' => ['digit', 'required']],
                'limit' => ['validators' => ['digit']],
                'offset' => ['validators' => ['digit']],
                'order_by' => ['validators' =>  [[
                    'name' => 'inclusion_in',
                    'params' => $allowed_order
                ]]],
                'order_direction' => ['validators' =>  [[
                    'name' => 'inclusion_in',
                    'params' => $allowed_order_direction
                ]]]
            ]
        );

        $instansi = Instansi::findFirst($params['id_instansi']);

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

        $order_direction = $params['order_direction'];
        $order_column = $params['order_by'];

        $rows = $this->rawQuery("
            select q.*,
                round((q.total_verified_question * 100.0) / NULLIF(q.total_answered_question,0), 2) as percent_verification,
                round((q.total_answered_question * 100.0) / NULLIF(q.total_question,0), 2) as percent_completion
            from (
                select s.id, id_periode, id_instansi_satker,id_survey_template,
                    (
                        select count(*)
                        from jaga.stranas_survey_template st
                            join jaga.stranas_survey_template_question stq on st.id = stq.id_survey_template
                        where st.id = s.id_survey_template
                    ) total_question,
                    (
                        select count(*)
                        from jaga.stranas_survey_template st
                            join jaga.stranas_survey_template_question stq on st.id = stq.id_survey_template
                            join jaga.stranas_survey_answer sa on sa.id_survey = s.id AND sa.id_question = stq.id_target
                        where st.id = s.id_survey_template
                    ) total_answered_question,
                    (
                        select count(*)
                        from jaga.stranas_survey_template st
                            join jaga.stranas_survey_template_question stq on st.id = stq.id_survey_template
                            join jaga.stranas_survey_answer sa on sa.id_survey = s.id AND sa.id_question = stq.id_target
                        where st.id = s.id_survey_template AND sa.status = :status_verified
                    ) total_verified_question
                from jaga.stranas_survey s
                where s.id_instansi_satker = :id_instansi
            ) q
            order by $order_column $order_direction NULLS LAST
            limit :limit
            offset :offset;
        ", [
            'id_instansi' => $params['id_instansi'],
            'limit' => $params['limit'],
            'offset' => $params['offset'],
            'status_verified' => StranasSurveyAnswer::STATUS_VERIFIED
        ]);

        $total_record = StranasSurvey::count(
            [
                "id_instansi_satker = :id_instansi:",
                "bind" => [
                    "id_instansi" => $params['id_instansi']
                ]
            ]
        );

        return new PaginatedResponse(
            $rows,
            $total_record,
            $params['limit'],
            $params['offset'],
            ['references' => ['instansi' => $instansi->toArray()]]
        );
    }

    public function my()
    {
        $this->checkScopes(['stranas.answer']);

        $current_instansi = $this->shared->user->instansi;

        if (empty($current_instansi)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'User tidak memiliki instansi');
        }

        if (!empty($current_instansi->deleted_at)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Instansi user telah dihapus/tidak aktif');
        }

        $activeSurveys = StranasSurvey::find([
            'conditions' => 'id_instansi_satker=:id_instansi: AND deleted_at is NULL',
            'bind' => [
                'id_instansi' => $current_instansi->id
            ]
        ]);

        $result = [];

        foreach ($activeSurveys as $key => $survey) {
            $result[] = $survey->getDetail();
        }

        return new CollectionPaginatedResponse($result);
    }

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

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

        $params = $this->validate(
            $rawBody,
            [
                'id_periode' => ['validators' => ['required']],
                'id_instansi_satker' => ['validators' => ['required']],
                'id_survey_template' => ['validators' => ['required']],
                'keterangan' => ['validators' => []]
            ]
        );

        $periode = StranasPeriode::findFirst($params['id_periode']);

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

        if (!empty($periode->deleted_at)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Periode sudah tidak aktif');
        }

        $instansi = Instansi::findFirst($params['id_instansi_satker']);

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

        if (!empty($instansi->deleted_at)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Instansi sudah tidak aktif');
        }

        $survey_template = StranasSurveyTemplate::findFirst($params['id_survey_template']);

        if (empty($survey_template)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Survey template tidak ditemukan');
        }

        if (!empty($survey_template->deleted_at)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Survey template sudah tidak aktif');
        }

        $activeSurvey = StranasSurvey::find([
            'conditions' => 'id_periode=:id_periode: AND id_instansi_satker=:id_instansi_satker: AND id_survey_template=:id_survey_template: AND deleted_at is NULL',
            'bind' => [
                'id_periode' => $params['id_periode'],
                'id_instansi_satker' => $params['id_instansi_satker'],
                'id_survey_template' => $params['id_survey_template']
            ]
        ]);

        if (count($activeSurvey) > 0) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Survey serupa sudah ada');
        }

        $survey = new StranasSurvey();
        $survey->id_periode = $params['id_periode'];
        $survey->id_instansi_satker = $params['id_instansi_satker'];
        $survey->id_survey_template = $params['id_survey_template'];
        $survey->keterangan = $params['keterangan'];
        $survey->created_by = $this->shared->user->um_id;
        $status = $survey->save();

        if (!$status) {
            $errors = $survey->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([$survey]);
        }
    }

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

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

        $params = $this->validate(
            $rawBody,
            [
                'id_periode' => ['validators' => ['required']],
                'id_instansi_list' => ['validators' => []],
                'id_survey_template' => ['validators' => ['required']],
                'keterangan' => ['validators' => []]
            ]
        );

        if (!is_array($params['id_instansi_list'])) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Format ID Instansi List tidak valid');
        }

        if (count($params['id_instansi_list']) < 1) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Instansi kosong');
        }

        $id_instansi_list = array_unique(array_values($params['id_instansi_list']));


        $periode = StranasPeriode::findFirst($params['id_periode']);

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

        if (!empty($periode->deleted_at)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Periode sudah tidak aktif');
        }

        $survey_template = StranasSurveyTemplate::findFirst($params['id_survey_template']);

        if (empty($survey_template)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Survey template tidak ditemukan');
        }

        if (!empty($survey_template->deleted_at)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Survey template sudah tidak aktif');
        }

        $instansi_list = Instansi::find([
            'conditions' => 'id in ({ids:array}) AND deleted_at is NULL',
            'bind' => [
                'ids' => array_values($id_instansi_list)
            ]
        ]);

        // Check Expected instansi List exists in master data

        $existing_instasi_list = [];

        foreach ($instansi_list as $key => $instansi) {
            $existing_instasi_list[] = $instansi->id;
        }

        $not_exist_ids = array_diff($id_instansi_list, $existing_instasi_list);

        if (!empty($not_exist_ids)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Instansi dengan ID ' . implode(",", $not_exist_ids) . ' tidak aktif/terdaftar');
        }

        $registered_instansi = StranasSurvey::find([
            'conditions' => 'id_survey_template=:id_survey_template: AND id_periode=:id_periode: AND deleted_at is NULL',
            'bind' => [
                'id_survey_template' => $params['id_survey_template'],
                'id_periode' => $params['id_periode']
            ]
        ]);

        $registered_instansi_ids = [];

        foreach ($registered_instansi as $key => $registered_survey) {
            $registered_instansi_ids[] = $registered_survey->id_instansi_satker;
        }

        // Register new surveys
        foreach ($existing_instasi_list as $key => $instansi_id) {
            if (!in_array($instansi_id, $registered_instansi_ids)) {
                $survey = new StranasSurvey();
                $survey->id_periode = $params['id_periode'];
                $survey->id_instansi_satker = $instansi_id;
                $survey->id_survey_template = $params['id_survey_template'];
                $survey->keterangan = $params['keterangan'];
                $survey->created_by = $this->shared->user->um_id;
                $status = $survey->save();
            }
        }

        return new CollectionPaginatedResponse([]);
    }

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

        $survey = StranasSurvey::findFirst($id);

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

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

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

        $params = $this->validate(
            $rawBody,
            [
                'id_periode' => ['validators' => ['required']],
                'id_instansi_satker' => ['validators' => ['required']],
                'id_survey_template' => ['validators' => ['required']],
                'keterangan' => ['validators' => []]
            ]
        );

        $periode = StranasPeriode::findFirst($params['id_periode']);

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

        if (!empty($periode->deleted_at)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Periode sudah tidak aktif');
        }

        $instansi = Instansi::findFirst($params['id_instansi_satker']);

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

        if (!empty($instansi->deleted_at)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Instansi sudah tidak aktif');
        }

        $survey_template = StranasSurveyTemplate::findFirst($params['id_survey_template']);

        if (empty($survey_template)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Survey template tidak ditemukan');
        }

        if (!empty($survey_template->deleted_at)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Survey template sudah tidak aktif');
        }

        $survey->id_periode = $params['id_periode'];
        $survey->id_instansi_satker = $params['id_instansi_satker'];
        $survey->id_survey_template = $params['id_survey_template'];
        $survey->keterangan = $params['keterangan'];
        $survey->updated_at = date('Y-m-d H:i:s');
        $survey->updated_by = $this->shared->user->um_id;
        $status = $survey->save();

        if (!$status) {
            $errors = $survey->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([$survey]);
        }
    }

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

        $survey = StranasSurvey::findFirst($id);

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

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

        $now = date('Y-m-d H:i:s');
        $survey->updated_at = $now;
        $survey->deleted_at = $now;
        $survey->updated_by = $this->shared->user->um_id;
        $status = $survey->save();

        if (!$status) {
            $errors = $survey->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([$survey]);
        }
    }

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

        $survey = StranasSurvey::findFirst($id);

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

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

        $now = date('Y-m-d H:i:s');
        $survey->updated_at = $now;
        $survey->deleted_at = null;
        $survey->updated_by = $this->shared->user->um_id;
        $status = $survey->save();

        if (!$status) {
            $errors = $survey->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([$survey]);
        }
    }

    public function detail($id)
    {
        $survey = StranasSurvey::findFirst($id);

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

        $detail = $survey->getDetail();

        return new CollectionPaginatedResponse([$detail]);
    }

    public function answer()
    {
        $this->checkScopes(['stranas.answer']);

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

        $params = $this->validate(
            $postParams,
            [
                'id_survey' => ['validators' => ['required', 'digit']],
                'id_target' => ['validator' => ['required', 'digit']],
                'nilai' => ['validator' => ['required', 'digit']],
                'keterangan' => ['validator' => []],
                'attachments' => ['validator' => []],
                'deleted_attachments' => ['validator' => []]
            ]
        );

        $survey = StranasSurvey::findFirst($params['id_survey']);

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

        if (!empty($survey->deleted_at)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Survey sudah tidak aktif');
        }

        if ($this->shared->user->id_instansi != $survey->id_instansi_satker) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Instansi user dan survey tidak sesuai');
        }

        $periode = $survey->stranasPeriode;

        if (empty($periode)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Periode survey tidak valid');
        }

        $started_at = new DateTime($periode->started_at);
        $ended_at = new DateTime($periode->ended_at);
        $now = new DateTime();

        if ($now > $ended_at) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Masa pengisian tidak valid: telah berakhir pada $periode->ended_at");
        }

        if ($now < $started_at) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Masa pengisian tidak valid: baru dimulai pada $periode->started_at");
        }

        $target = StranasTarget::findFirst($params['id_target']);

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

        if (!empty($target->deleted_at)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Target sudah tidak aktif');
        }

        if ($params['nilai'] < 0 || $params['nilai'] > 100) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Nilai tidak valid (0-100)');
        }

        if (!empty($params['attachments']) && !is_array($params['attachments'])) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Format Attachments tidak valid');
        }

        if (!empty($params['deleted_attachments']) && !is_array($params['deleted_attachments'])) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Format Deleted Attachments tidak valid');
        }

        $answer = null;

        $records = StranasSurveyAnswer::find([
            'conditions' => 'id_survey=:id_survey: AND id_question=:id_target:',
            'bind' => [
                'id_survey' => $params['id_survey'],
                'id_target' => $params['id_target']
            ]
        ]);

        if (count($records) > 0) {
            $answer = $records[0];

            if ($answer->status != StranasSurveyAnswer::STATUS_INIT) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Jawaban sudah diverifikasi');
            }

            $answer->nilai = $params['nilai'];
            $answer->keterangan = $params['keterangan'];
            $answer->updated_at = date('Y-m-d H:i:s');
            $answer->updated_by = $this->shared->user->um_id;
            $status = $answer->save();
        } else {
            $answer = new StranasSurveyAnswer();
            $answer->id_survey = $params['id_survey'];
            $answer->id_question = $params['id_target'];
            $answer->nilai = $params['nilai'];
            $answer->keterangan = $params['keterangan'];
            $answer->created_by = $this->shared->user->um_id;
            $answer->status = StranasSurveyAnswer::STATUS_INIT;
            $status =  $answer->save();
        }

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

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

            throw new ValidationException($error_messages);
        }

        if (!empty($params['deleted_attachments'])) {
            foreach ($params['deleted_attachments'] as $uuid) {
                $attachment_record = Attachments::findFirstByUuid($uuid);

                if (!empty($attachment_record)) {
                    $stranas_attachment_record = StranasSurveyAnswerAttachment::findFirstByIdAttachment($attachment_record->id);

                    if (!empty($stranas_attachment_record) && $stranas_attachment_record->tipe == StranasSurveyAnswerAttachment::TIPE_JAWABAN) {
                        $stranas_attachment_record->delete();
                    }

                    AttachmentHelper::delete($attachment_record, true, $this->minio);
                }
            }
        }

        if (!empty($params['attachments'])) {
            foreach ($params['attachments'] as $key => $uuid) {
                $attachment_record = Attachments::findFirstByUuid($uuid);

                if (!empty($attachment_record) && empty($attachment_record->deleted_at)) {
                    $existing_stranas_attachments = StranasSurveyAnswerAttachment::find([
                        'conditions' => 'id_answer=:id_answer: AND id_attachment=:id_attachment:',
                        'bind' => [
                            'id_answer' => $answer->id,
                            'id_attachment' => $attachment_record->id
                        ]
                    ]);

                    if (count($existing_stranas_attachments) < 1) {
                        $stranas_attachment = new StranasSurveyAnswerAttachment();
                        $stranas_attachment->id_answer = $answer->id;
                        $stranas_attachment->id_attachment = $attachment_record->id;
                        $stranas_attachment->tipe = StranasSurveyAnswerAttachment::TIPE_JAWABAN;
                        $stranas_attachment->created_by = $this->shared->user->um_id;
                        $stranas_attachment->save();

                        $attachment_record->status = Attachments::STATUS_REFERRED;
                        $attachment_record->referer = Attachments::REFERER_STRANAS_ANSWER;
                        $attachment_record->save();
                    }
                }
            }
        }

        return new CollectionPaginatedResponse([$answer]);
    }

    public function comment()
    {
        $this->checkScopes(['stranas.comment']);

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

        $params = $this->validate(
            $postParams,
            [
                'id_answer' => ['validators' => ['required', 'digit']],
                'content' => ['validator' => ['required']],
                'attachments' => ['validator' => []]
            ]
        );

        $answer = StranasSurveyAnswer::findFirst($params['id_answer']);

        if (empty($answer)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Jawaban Survey tidak ditemukan');
        }

        if (!empty($answer->deleted_at)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Jawaban Survey sudah tidak aktif');
        }

        if (!empty($params['attachments']) && !is_array($params['attachments'])) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Format Attachments tidak valid');
        }

        $comment = new StranasSurveyAnswerComment();
        $comment->id_answer = $params['id_answer'];
        $comment->content = $params['content'];
        $comment->created_by = $this->shared->user->um_id;
        $status =  $comment->save();

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

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

            throw new ValidationException($error_messages);
        }

        if (!empty($params['attachments'])) {
            foreach ($params['attachments'] as $key => $uuid) {
                $attachment_record = Attachments::findFirstByUuid($uuid);

                if (!empty($attachment_record) && empty($attachment_record->deleted_at)) {
                    $existing_stranas_attachments = StranasSurveyAnswerCommentAttachment::find([
                        'conditions' => 'id_comment=:id_comment: AND id_attachment=:id_attachment:',
                        'bind' => [
                            'id_comment' => $comment->id,
                            'id_attachment' => $attachment_record->id
                        ]
                    ]);

                    if (count($existing_stranas_attachments) < 1) {
                        $comment_attachment = new StranasSurveyAnswerCommentAttachment();
                        $comment_attachment->id_comment = $comment->id;
                        $comment_attachment->id_attachment = $attachment_record->id;
                        $comment_attachment->created_by = $this->shared->user->um_id;
                        $comment_attachment->save();

                        $attachment_record->status = Attachments::STATUS_REFERRED;
                        $attachment_record->referer = Attachments::REFERER_STRANAS_ANSWER;
                        $attachment_record->save();
                    }
                }
            }
        }

        return new CollectionPaginatedResponse([$comment]);
    }

    public function comment_update()
    {
        $this->checkScopes(['stranas.comment']);

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

        $params = $this->validate(
            $postParams,
            [
                'id_comment' => ['validators' => ['required', 'digit']],
                'content' => ['validator' => ['required']],
                'attachments' => ['validator' => []],
                'deleted_attachments' => ['validator' => []]
            ]
        );

        $comment = StranasSurveyAnswerComment::findFirst($params['id_comment']);

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

        if (!empty($comment->deleted_at)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Komentar sudah tidak aktif');
        }

        if ($this->shared->user->um_id != $comment->created_by) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'ID pembuat komentar tidak sama dengan user aktif');
        }

        if (!empty($params['attachments']) && !is_array($params['attachments'])) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Format Attachments tidak valid');
        }

        if (!empty($params['deleted_attachments']) && !is_array($params['deleted_attachments'])) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Format Deleted Attachments tidak valid');
        }


        $comment->content = $params['content'];
        $comment->updated_at = date('Y-m-d H:i:s');
        $comment->updated_by = $this->shared->user->um_id;
        $status = $comment->save();

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

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

            throw new ValidationException($error_messages);
        }

        if (!empty($params['deleted_attachments'])) {
            foreach ($params['deleted_attachments'] as $uuid) {
                $attachment_record = Attachments::findFirstByUuid($uuid);

                if (!empty($attachment_record)) {
                    $stranas_attachment_comment_record = StranasSurveyAnswerCommentAttachment::findFirstByIdAttachment($attachment_record->id);

                    if (!empty($stranas_attachment_comment_record)) {
                        $stranas_attachment_comment_record->delete();
                    }

                    AttachmentHelper::delete($attachment_record, true, $this->minio);
                }
            }
        }

        if (!empty($params['attachments'])) {
            foreach ($params['attachments'] as $key => $uuid) {
                $attachment_record = Attachments::findFirstByUuid($uuid);

                if (!empty($attachment_record) && empty($attachment_record->deleted_at)) {
                    $existing_stranas_comment_attachments = StranasSurveyAnswerCommentAttachment::find([
                        'conditions' => 'id_comment=:id_comment: AND id_attachment=:id_attachment:',
                        'bind' => [
                            'id_comment' => $comment->id,
                            'id_attachment' => $attachment_record->id
                        ]
                    ]);

                    if (count($existing_stranas_comment_attachments) < 1) {
                        $comment_attachment = new StranasSurveyAnswerCommentAttachment();
                        $comment_attachment->id_comment = $comment->id;
                        $comment_attachment->id_attachment = $attachment_record->id;
                        $comment_attachment->created_by = $this->shared->user->um_id;
                        $comment_attachment->save();

                        $attachment_record->status = Attachments::STATUS_REFERRED;
                        $attachment_record->referer = Attachments::REFERER_STRANAS_ANSWER;
                        $attachment_record->save();
                    }
                }
            }
        }

        return new CollectionPaginatedResponse([$comment]);
    }

    public function verify()
    {
        $this->checkScopes(['stranas.verify']);

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

        $params = $this->validate(
            $postParams,
            [
                'id_survey' => ['validators' => ['required', 'digit']],
                'id_target' => ['validators' => ['required', 'digit']],
                'nilai_verification' => ['validators' => ['required', 'digit']],
                'keterangan_verification' => ['validators' => []],
                'attachments' => ['validators' => []],
                'deleted_attachments' => ['validators' => []]
            ]
        );

        $survey = StranasSurvey::findFirst($params['id_survey']);

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

        if (!empty($survey->deleted_at)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Survey sudah tidak aktif');
        }

        $periode = $survey->stranasPeriode;

        if (empty($periode)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Periode survey tidak valid');
        }


        if (!empty($periode->verification_started_at) && !empty($periode->verification_ended_at)) {
            $verification_started_at = new DateTime($periode->verification_started_at);
            $verification_ended_at = new DateTime($periode->verification_ended_at);

            $now = new DateTime();

            if ($now > $verification_ended_at) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Masa pengisian tidak valid: telah berakhir pada $periode->verification_ended_at");
            }

            if ($now < $verification_started_at) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Masa pengisian tidak valid: baru dimulai pada $periode->verification_started_at");
            }
        }

        $target = StranasTarget::findFirst($params['id_target']);

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

        if (!empty($target->deleted_at)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Target sudah tidak aktif');
        }

        if ($params['nilai_verification'] < 0 || $params['nilai_verification'] > 100) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Nilai tidak valid (0-100)');
        }

        if (!empty($params['attachments']) && !is_array($params['attachments'])) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Format Attachments tidak valid');
        }

        if (!empty($params['deleted_attachments']) && !is_array($params['deleted_attachments'])) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Format Deleted Attachments tidak valid');
        }

        $answer = null;
        $now = date('Y-m-d H:i:s');

        $records = StranasSurveyAnswer::find([
            'conditions' => 'id_survey=:id_survey: AND id_question=:id_target:',
            'bind' => [
                'id_survey' => $params['id_survey'],
                'id_target' => $params['id_target']
            ]
        ]);

        if (count($records) > 0) {
            $answer = $records[0];
        } else {
            $answer = new StranasSurveyAnswer();
            $answer->id_survey = $params['id_survey'];
            $answer->id_question = $params['id_target'];
        }

        $answer->nilai_verification = $params['nilai_verification'];
        $answer->keterangan_verification = $params['keterangan_verification'];
        $answer->status = StranasSurveyAnswer::STATUS_VERIFIED;
        $answer->verified_at = $now;
        $answer->verified_by = $this->shared->user->um_id;
        $status = $answer->save();

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

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

            throw new ValidationException($error_messages);
        }

        if (!empty($params['deleted_attachments'])) {
            foreach ($params['deleted_attachments'] as $uuid) {
                $attachment_record = Attachments::findFirstByUuid($uuid);

                if (!empty($attachment_record)) {
                    $stranas_attachment_record = StranasSurveyAnswerAttachment::findFirstByIdAttachment($attachment_record->id);

                    if (!empty($stranas_attachment_record) && $stranas_attachment_record->tipe == StranasSurveyAnswerAttachment::TIPE_VERIFIKASI) {
                        $stranas_attachment_record->delete();
                    }

                    AttachmentHelper::delete($attachment_record, true, $this->minio);
                }
            }
        }

        if (!empty($params['attachments'])) {
            foreach ($params['attachments'] as $key => $uuid) {
                $attachment_record = Attachments::findFirstByUuid($uuid);

                if (!empty($attachment_record) && empty($attachment_record->deleted_at)) {
                    $existing_stranas_attachments = StranasSurveyAnswerAttachment::find([
                        'conditions' => 'id_answer=:id_answer: AND id_attachment=:id_attachment:',
                        'bind' => [
                            'id_answer' => $answer->id,
                            'id_attachment' => $attachment_record->id
                        ]
                    ]);

                    if (count($existing_stranas_attachments) < 1) {
                        $stranas_attachment = new StranasSurveyAnswerAttachment();
                        $stranas_attachment->id_answer = $answer->id;
                        $stranas_attachment->id_attachment = $attachment_record->id;
                        $stranas_attachment->tipe = StranasSurveyAnswerAttachment::TIPE_VERIFIKASI;
                        $stranas_attachment->created_by = $this->shared->user->um_id;
                        $stranas_attachment->save();

                        $attachment_record->status = Attachments::STATUS_REFERRED;
                        $attachment_record->referer = Attachments::REFERER_STRANAS_ANSWER;
                        $attachment_record->save();
                    }
                }
            }
        }

        return new CollectionPaginatedResponse([$answer->getDetail()]);
    }

    public function downloadAttachment($uuid)
    {
        $attachment = StranasSurveyAnswerAttachment::findFirst(
            [
                "uuid = :uuid:",
                "bind" => [
                    "uuid" => $uuid
                ]
            ]
        );
        if (!empty($attachment)) {
            $attachment_folder = getenv("USER_IMAGE_FOLDER") . DIRECTORY_SEPARATOR . 'stranas' . DIRECTORY_SEPARATOR;
            $filename = $attachment_folder . DIRECTORY_SEPARATOR . $uuid;

            if (!file_exists($filename)) {
                throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "File Not Found");
            }

            return MediaHelper::fileResponse($filename, $attachment->filename);
        } else {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Attachment Not Found");
        }
    }
}
