<?php

class LitbangKajianPengajuanController extends BaseController {
    public function index() {
        $this->checkScopes(['litbang.access']);
        $params = $this->request->get();
        $params['satgas_id'] = array_key_exists('satgas_id', $params) ? $params['satgas_id'] : -1;
        $params['sektor_id'] = array_key_exists('sektor_id', $params) ? $params['sektor_id'] : -1;
        $params['jenis_id'] = array_key_exists('jenis_id', $params) ? $params['jenis_id'] : -1;
        $params['judul'] = array_key_exists('judul', $params) ? $params['judul'] : "";
        $params['status'] = array_key_exists('status', $params) ? $params['status'] : "%%";
        $params['tahapan'] = array_key_exists('tahapan', $params) ? $params['tahapan'] : "%%";
        $params['timeline'] = array_key_exists('timeline', $params) ? $params['timeline'] : -1;
        $params['tahun'] = array_key_exists('tahun', $params) ? $params['tahun'] : 0;
        $params['limit'] = $this->getDefaultLimit($params);
        $params['offset'] = array_key_exists('offset', $params) ? $params['offset'] : 0;
        $params['order_by'] = array_key_exists('order_by', $params) ? strtolower($params['order_by']) : 'id';
        $params['order_direction'] = array_key_exists('order_direction', $params) ? strtolower($params['order_direction']) : 'asc';

        $allowed_order = [
            'id',
            'satgas_id',
            'sektor_id',
            'jenis_id',
            'judul',
            'status',
            'tahapan',
            'created_at',
            'updated_at'
        ];
        $allowed_order_direction = ['asc', 'desc'];

        $this->validate(
            $params,
            [
                'satgas_id' => ['validators' => ['digit']],
                'sektor_id' => ['validators' => ['digit']],
                'jenis_id' => ['validators' => ['digit']],
                'judul' => ['validators' => []],
                'status' => ['validators' => []],
                'tahapan' => ['validators' => []],
                'timeline' => ['validators' => ['digit']],
                'tahun' => ['validators' => ['digit']],
                '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
                ]]]
            ]
        );

        if ($this->hasOneOfRoles(['anggota_litbang', 'kasatgas_litbang'])) {
            /** @var $litbangUser LitbangUser */
            $litbangUser = LitbangUser::findOrFail("um_id = " . $this->userID);
            $params['satgas_id'] = $litbangUser->satgas_id;
        }

        // filter apakah pengajuan ontime/offtime
        // 0 => offtime
        // 1 => ontime
        $qTimeline = "";
        if ($params['timeline'] > -1) {
            if ($params['timeline'] == 0) {
                $qTimeline = " AND pap_tgl_realisasi > pap_tgl_selesai";
            } elseif ($params['timeline'] == 1) {
                $qTimeline = " AND pap_tgl_realisasi <= pap_tgl_selesai";
            }
        }

        $conditions = [
            'conditions' => "
                (sektor_id = :sektor_id: OR 0 > :sektor_id:) AND 
                (satgas_id = :satgas_id: OR 0 > :satgas_id:) AND 
                (jenis_id = :jenis_id: OR 0 > :jenis_id:) AND 
                judul ILIKE :judul: AND 
                status LIKE :status: AND 
                tahapan LIKE :tahapan:
                ". ($params['tahun'] > 0 ? "AND date_part('year', created_at) = " . $params['tahun'] : "") . $qTimeline ."
            ",
            'bind' => [
                'satgas_id' => $params['satgas_id'],
                'sektor_id' => $params['sektor_id'],
                'jenis_id' => $params['jenis_id'],
                'judul' => "%" . $params['judul'] . "%",
                'status' => $params['status'],
                'tahapan' => $params['tahapan'],
            ],
            'order' =>  $params['order_by'] . " " . $params['order_direction'],
        ];

        $data =  LitbangKajianPengajuan::find(array_merge($conditions, [
            'limit' => $params['limit'],
            'offset' => $params['offset']]
        ));

        $result = [];
        foreach ($data as $row) {
            /** @var $row LitbangKajianPengajuan  */
            array_push($result, $row->getDetail());
        }

        $total = LitbangKajianPengajuan::find($conditions)->count();

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

    public function create() {
        $this->checkScopes(['litbang.submit']);
        $params = $this->validatePt($this->request->getJsonRawBody(true));

        $record = new LitbangKajianPengajuan();
        $record->assign($params);

        $status = $record->save();

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

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

            throw new ValidationException($error_messages);
        } else {
            if (!empty($params['attachments'])) {
                foreach ($params['attachments'] as $attachment) {
                    $attachment_record = Attachments::findFirst($attachment['attachment_id']);
                    if (!empty($attachment_record) && empty($attachment_record->deleted_at)) {
                        $attachment_record->status = Attachments::STATUS_REFERRED;
                        $attachment_record->referer = Attachments::REFERER_LITBANG_KAJIAN_PENGAJUAN;
                        $attachment_record->save();

                        $attachment_kajian = LitbangKajianPengajuanAttachment::firstOrCreate("attachment_id = " . $attachment_record->id);
                        $attachment_kajian->attachment_id = $attachment_record->id;
                        $attachment_kajian->kajian_pengajuan_id = $record->id;
                        $attachment_kajian->tahapan = $record->tahapan;
                        $attachment_kajian->jenis_dokumen_id = $attachment['jenis_dokumen_id'];
                        $attachment_kajian->created_by = $this->userID;
                        $attachment_kajian->updated_by = $this->userID;
                        $attachment_kajian->save();
                    }
                }
            }

            return new CollectionPaginatedResponse([$record]);
        }
    }

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

        /** @var $record LitbangKajianPengajuan */
        $record = LitbangKajianPengajuan::findFirst($id);

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

        $satgasId = $this->validateSatgas()['satgas_id'];
        if ($satgasId > 0 && $satgasId != $record->satgas_id) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'User tidak berhak melakukan aksi ini');
        }

        $rawBody = $this->request->getJsonRawBody(true);
        switch ($record->tahapan) {
            case LitbangKajianPengajuan::TAHAPAN_PT:
                $params = $this->validatePt($rawBody, $record);
                break;
            case LitbangKajianPengajuan::TAHAPAN_RKK:
                $params = $this->validateRkk($rawBody, $record);
                break;
            case LitbangKajianPengajuan::TAHAPAN_KRS:
                $params = $this->validateKrs($rawBody, $record);
                break;
            case LitbangKajianPengajuan::TAHAPAN_KOM:
                $params = $this->validateKom($rawBody, $record);
                break;
            case LitbangKajianPengajuan::TAHAPAN_PK:
                $params = $this->validatePk($rawBody, $record);
                break;
            case LitbangKajianPengajuan::TAHAPAN_KL:
                $params = $this->validateKl($rawBody, $record);
                break;
            case LitbangKajianPengajuan::TAHAPAN_RD:
                $params = $this->validateRd($rawBody, $record);
                break;
            case LitbangKajianPengajuan::TAHAPAN_PAP:
                $params = $this->validatePap($rawBody, $record);
                break;
            default:
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Tahapan tidak valid!');
        }

        $attachment_ids = [];
        if (!empty($params['attachments'])) {
            foreach ($params['attachments'] as $attachment) {
                $attachment_record = Attachments::findFirst($attachment['attachment_id']);
                if (!empty($attachment_record) && empty($attachment_record->deleted_at)) {
                    $attachment_record->status = Attachments::STATUS_REFERRED;
                    $attachment_record->referer = Attachments::REFERER_LITBANG_KAJIAN_PENGAJUAN;
                    $attachment_record->save();

                    $attachment_kajian = LitbangKajianPengajuanAttachment::firstOrCreate("attachment_id = " . $attachment_record->id);
                    $attachment_kajian->kajian_pengajuan_id = $record->id;
                    $attachment_kajian->attachment_id = $attachment_record->id;
                    $attachment_kajian->jenis_dokumen_id = $attachment['jenis_dokumen_id'];
                    if ($attachment_kajian->isNewRecord()) {
                        $attachment_kajian->created_by = $this->userID;
                        $attachment_kajian->tahapan = $record->tahapan;
                    } else {
                        $attachment_kajian->updated_at = date('Y-m-d H:i:s');
                        $attachment_kajian->updated_by = $this->userID;
                    }
                    $attachment_kajian->save();
                    array_push($attachment_ids, $attachment_kajian->attachment_id);
                }
            }
        }

        $record->assign($params);
        $record->updated_at = date('Y-m-d H:i:s');
        $status = $record->save();

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

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

            throw new ValidationException($error_messages);
        } else {
            $record->syncAttachments($attachment_ids, $record->tahapan);
            return new CollectionPaginatedResponse([$record]);
        }
    }

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

        /** @var $record LitbangKajianPengajuan */
        $record = LitbangKajianPengajuan::findFirst($id);

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

        if ($record->status != LitbangKajianPengajuan::STATUS_PROCESS) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Kajian belum di-submit!');
        }

        $rawBody = $this->request->getJsonRawBody(true);
        $params = $this->validate(
            $rawBody,
            [
                "approval_status" => ['validators' => ['required', 'digit']],
                "approval_note" => ['validators' => []],
            ]
        );

        $approvalStatus = $params['approval_status'];
        if ($approvalStatus == LitbangKajianPengajuan::APPROVED) {
            $record->approval_note = null;
            $record->status = LitbangKajianPengajuan::STATUS_DRAFT;
            $record->tahapan = $this->moveTahapan($record->tahapan);
        } elseif ($approvalStatus == LitbangKajianPengajuan::REJECTED) {
            $record->approval_note = $params['approval_note'];
            $record->status = LitbangKajianPengajuan::STATUS_REJECTED;
        } else {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Status Persetujuan tidak valid!');
        }

        $this->db->begin();
        try {
            // ketika tahapan PUBLISHED, clone data pengajuan ke repositori
            if ($record->tahapan == LitbangKajianPengajuan::TAHAPAN_PUB) {
                $record->status = LitbangKajianPengajuan::STATUS_PUBLISHED;
                LitbangService::createRepositoriFromPengajuan($record, $this->userID);
            }

            $status = $record->save();

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

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

                throw new ValidationException($error_messages);
            } else {
                $this->db->commit();
                return new CollectionPaginatedResponse([$record]);
            }
        } catch (\Exception $e) {
            $this->db->rollback();
            throw $e;
        }
    }

    public function uploadDocument()
    {
        $this->checkScopes(['litbang.submit']);
        $postParams = $this->request->getPost();

        $this->validate($postParams, [
            'keterangan' => ['validators' => []],
            'metadata' => ['validators' => []]
        ]);

        $result = LitbangService::uploadDocument(
            $this->shared,
            $this->minio,
            $this->request,
            LitbangKajianPengajuan::DOCUMENT_DIR,
            $this->config["allowed_mime_types"],
            5
        );

        return new CollectionPaginatedResponse($result);
    }

    public function downloadDocument($uuid)
    {
        $this->checkScopes(['litbang.access']);
        return LitbangService::downloadAttachment(
            $this->shared,
            $this->minio,
            LitbangKajianPengajuan::DOCUMENT_DIR,
            $uuid
        );
    }

    public function detail($id)
    {
        $this->checkScopes(['litbang.access']);
        /** @var $result LitbangKajianPengajuan */
        $result = LitbangKajianPengajuan::findFirst([
            'conditions' => 'id = :id: AND deleted_at is null',
            'bind' => [
                'id' => $id
            ]
        ]);

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

        return new SimpleResponse($result->getDetail());
    }

    public function dashboard() {
        $this->checkScopes(['litbang.access']);
        $params = $this->validateSatgas();
        $satgasId = $params['satgas_id'];

        $result = [
            'tahapan'           => $this->countByTahapan($satgasId),
            'sektor'            => $this->countBySektor(),
            'timeline'          => $this->countByTimeline($satgasId),
            'timelineByTahapan' => $this->countTimelineByTahapan($satgasId)
        ];

        return new SimpleResponse($result);
    }

    public function count()
    {
        $this->checkScopes(['litbang.access']);
        $params = $this->validateSatgas();
        $result = $this->countByTahapan($params['satgas_id']);

        return new SimpleResponse($result);
    }

    public function listInstansi()
    {
        $this->checkScopes(['litbang.access']);
        $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'] : '';

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

        $instansiList = Instansi::find([
            'conditions' => 'nama ilike :keyword:',
            'bind' => [
                'keyword' => '%' . $params['keyword'] . '%'
            ],
            'limit' => $params['limit'],
            'offset' => $params['offset']
        ]);

        $total_instansi = Instansi::count();

        return new PaginatedResponse($instansiList, $total_instansi, $params['limit'], $params['offset']);
    }

    public function jenisDokumenByTahapan($tahapan)
    {
        $this->checkScopes(['litbang.access']);
        $result = LitbangKajianPengajuanJenisDokumen::findByTahapan($tahapan);
        return new CollectionPaginatedResponse($result);
    }

    private function countByTahapan($satgasId) {
        $tahapans = [
            LitbangKajianPengajuan::TAHAPAN_PT,
            LitbangKajianPengajuan::TAHAPAN_RKK,
            LitbangKajianPengajuan::TAHAPAN_KRS,
            LitbangKajianPengajuan::TAHAPAN_KOM,
            LitbangKajianPengajuan::TAHAPAN_PK,
            LitbangKajianPengajuan::TAHAPAN_KL,
            LitbangKajianPengajuan::TAHAPAN_RD,
            LitbangKajianPengajuan::TAHAPAN_PAP
        ];
        $qArrayTahapan = [];
        foreach ($tahapans as $tahapan) {
            array_push($qArrayTahapan, "COUNT(id) FILTER (WHERE tahapan = '" . $tahapan . "') AS " . $tahapan);
        }
        $query = "SELECT " . implode(",", $qArrayTahapan) . " FROM litbang_kajian_pengajuan WHERE (0 > ".$satgasId." OR satgas_id = ".$satgasId.") AND deleted_at IS NULL";
        $result = $this->rawQuery($query);

        return $result[0];
    }

    private function countBySektor() {
        $sektors = LitbangKajianSektor::find([
            'conditions' => 'deleted_at IS NULL'
        ]);

        $rawResult = [];
        $qArray = [];
        foreach ($sektors as $sektor) {
            /** @var $sektor LitbangKajianSektor */
            array_push($qArray, "COUNT(id) FILTER (WHERE sektor_id = " . $sektor->id . ") AS sektor" . $sektor->id);
            $rawResult[$sektor->id] = [
                'id' => $sektor->id,
                'nama' => $sektor->nama,
                'count' => 0
            ];
        }
        $data = $this->rawQuery("SELECT " . implode(",", $qArray). " FROM litbang_kajian WHERE deleted_at IS NULL");

        $result = [];
        if (sizeof($data) > 0) {
            foreach ($data[0] as $key => $row) {
                $rawResult[str_replace('sektor', '', $key)]['count'] = $row;
                array_push($result, $rawResult[str_replace('sektor', '', $key)]);
            }
        }

        return $result;
    }

    private function countByTimeline($satgasId) {
        $count = $this->rawQuery("
            SELECT lkp.satgas_id, ls.nama,
                COUNT(lkp.id) FILTER (WHERE lkp.pap_tgl_realisasi <= lkp.pap_tgl_selesai) AS ontime,
                COUNT(lkp.id) FILTER (WHERE lkp.pap_tgl_realisasi > lkp.pap_tgl_selesai) AS offtime
            FROM litbang_kajian_pengajuan lkp LEFT JOIN litbang_satgas ls ON ls.id = lkp.satgas_id
            WHERE ".($satgasId > 0 ? "lkp.satgas_id = " . $satgasId . " AND " : "")." lkp.status = '".LitbangKajianPengajuan::STATUS_PUBLISHED."'
            GROUP BY lkp.satgas_id, ls.nama
        ");

        return $count;
    }

    private function countTimelineByTahapan($satgasId) {
        return $this->rawQuery("
            SELECT lkp.satgas_id, ls.nama,
                COUNT(lkp.id) FILTER (WHERE lkp.krs_tgl_realisasi <= lkp.krs_tgl_selesai AND lkp.krs_tgl_realisasi IS NOT NULL) AS krs_ontime,
                COUNT(lkp.id) FILTER (WHERE lkp.krs_tgl_realisasi > lkp.krs_tgl_selesai AND lkp.krs_tgl_realisasi IS NOT NULL) AS krs_offtime,
                COUNT(lkp.id) FILTER (WHERE lkp.kom_tgl_realisasi <= lkp.kom_tgl_selesai AND lkp.kom_tgl_realisasi IS NOT NULL) AS kom_ontime,
                COUNT(lkp.id) FILTER (WHERE lkp.kom_tgl_realisasi > lkp.kom_tgl_selesai AND lkp.kom_tgl_realisasi IS NOT NULL) AS kom_offtime,
                COUNT(lkp.id) FILTER (WHERE lkp.pk_tgl_realisasi <= lkp.pk_tgl_selesai AND lkp.pk_tgl_realisasi IS NOT NULL) AS pk_ontime,
                COUNT(lkp.id) FILTER (WHERE lkp.pk_tgl_realisasi > lkp.pk_tgl_selesai AND lkp.pk_tgl_realisasi IS NOT NULL) AS pk_offtime,
                COUNT(lkp.id) FILTER (WHERE lkp.kl_tgl_realisasi <= lkp.kl_tgl_selesai AND lkp.kl_tgl_realisasi IS NOT NULL) AS kl_ontime,
                COUNT(lkp.id) FILTER (WHERE lkp.kl_tgl_realisasi > lkp.kl_tgl_selesai AND lkp.kl_tgl_realisasi IS NOT NULL) AS kl_offtime,
                COUNT(lkp.id) FILTER (WHERE lkp.rd_tgl_realisasi <= lkp.rd_tgl_selesai AND lkp.rd_tgl_realisasi IS NOT NULL) AS rd_ontime,
                COUNT(lkp.id) FILTER (WHERE lkp.rd_tgl_realisasi > lkp.rd_tgl_selesai AND lkp.rd_tgl_realisasi IS NOT NULL) AS rd_offtime,
                COUNT(lkp.id) FILTER (WHERE lkp.pap_tgl_realisasi <= lkp.pap_tgl_selesai AND lkp.pap_tgl_realisasi IS NOT NULL) AS pap_ontime,
                COUNT(lkp.id) FILTER (WHERE lkp.pap_tgl_realisasi > lkp.pap_tgl_selesai AND lkp.pap_tgl_realisasi IS NOT NULL) AS pap_offtime
            FROM litbang_kajian_pengajuan lkp LEFT JOIN litbang_satgas ls ON ls.id = lkp.satgas_id
            WHERE lkp.satgas_id = ".$satgasId." OR lkp.satgas_id > ".$satgasId."
            GROUP BY lkp.satgas_id, ls.nama
        ");
    }

    private function moveTahapan($tahapan, $isUp = true) {
        $nextTahapan = $tahapan;
        switch ($tahapan) {
            case LitbangKajianPengajuan::TAHAPAN_PT:
                $nextTahapan = LitbangKajianPengajuan::TAHAPAN_RKK;
                break;
            case LitbangKajianPengajuan::TAHAPAN_RKK:
                $nextTahapan = LitbangKajianPengajuan::TAHAPAN_KRS;
                break;
            case LitbangKajianPengajuan::TAHAPAN_KRS:
                $nextTahapan = LitbangKajianPengajuan::TAHAPAN_KOM;
                break;
            case LitbangKajianPengajuan::TAHAPAN_KOM:
                $nextTahapan = LitbangKajianPengajuan::TAHAPAN_PK;
                break;
            case LitbangKajianPengajuan::TAHAPAN_PK:
                $nextTahapan = LitbangKajianPengajuan::TAHAPAN_KL;
                break;
            case LitbangKajianPengajuan::TAHAPAN_KL:
                $nextTahapan = LitbangKajianPengajuan::TAHAPAN_RD;
                break;
            case LitbangKajianPengajuan::TAHAPAN_RD:
                $nextTahapan = LitbangKajianPengajuan::TAHAPAN_PAP;
                break;
            case LitbangKajianPengajuan::TAHAPAN_PAP:
                $nextTahapan = LitbangKajianPengajuan::TAHAPAN_PUB;
                break;
            default:
                break;
        }

        return $nextTahapan;
    }

    private function validateSatgas() {
        $params = $this->request->get();
        $params['satgas_id'] = array_key_exists('satgas_id', $params) ? $params['satgas_id'] : -1;
        $params = $this->validate($params, [
            'satgas_id' => ['validators' => ['digit']],
        ]);

        if ($this->hasOneOfRoles(['anggota_litbang', 'kasatgas_litbang'])) {
            /** @var $litbangUser LitbangUser */
            $litbangUser = LitbangUser::findFirst("um_id = " . $this->userID);
            if ($litbangUser) {
                $params['satgas_id'] = $litbangUser->satgas_id;
            }
        }

        return $params;
    }

    private function validatePt($rawBody, $kajian = null) {
        if ($kajian) {
            if ($kajian->status == LitbangKajianPengajuan::STATUS_PROCESS) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Kajian dengan status ' . LitbangKajianPengajuan::STATUS_PROCESS . ' tidak dapat diubah');
            }
        }

        $rawBody['submit_status'] = (array_key_exists('submit_status', $rawBody) ? $rawBody['submit_status'] : LitbangKajianPengajuan::STATUS_DRAFT);
        $params = $this->validate(
            $rawBody,
            [
                "sektor_id" => ['validators' => ['required', 'digit']],
                "jenis_id" => ['validators' => ['required', 'digit']],
                "judul" => ['validators' => ['required']],
                "pt_pertimbangan_pemilihan_topik" => ['validators' => ['required']],
                "pt_daftar_peneliti" => ['validators' => ['required']],
                "pt_kontak_peneliti" => ['validators' => ['required']],
                "pt_assign_to" => ['validators' => ['required', 'digit']],
                "submit_status" => ['validators' => ['required']],
                "attachments" => ['validators' => []],
            ]
        );
        $this->validateJenisDokumen(LitbangKajianPengajuan::TAHAPAN_PT, $params['attachments']);

        $sektor = LitbangKajianSektor::findFirst([
            'conditions' => 'id = :sektor_id: AND deleted_at is null',
            'bind' => [
                'sektor_id' => $params['sektor_id']
            ]
        ]);

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

        $jenis = LitbangKajianJenis::findFirst([
            'conditions' => 'id = :jenis_id: AND deleted_at is null',
            'bind' => [
                'jenis_id' => $params['jenis_id']
            ]
        ]);

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

        if ($this->userID) {
            /** @var $userLitbang LitbangUser */
            $userLitbang = LitbangUser::findFirst([
                'conditions' => 'um_id = :id:',
                'bind' => [
                    'id' => $this->userID
                ]
            ]);

            $params['satgas_id'] = $userLitbang->satgas_id;

            if (!$userLitbang) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'User litbang tidak ditemukan');
            }
        } else {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'User litbang tidak ditemukan');
        }

        $params['pt_created_by'] = $this->userID;
        $params['status'] = $params['submit_status'] == LitbangKajianPengajuan::STATUS_PROCESS ? LitbangKajianPengajuan::STATUS_PROCESS : LitbangKajianPengajuan::STATUS_DRAFT;
        $params['tahapan'] = LitbangKajianPengajuan::TAHAPAN_PT;

        return $params;
    }

    private function validateRkk($rawBody, $kajian = null)
    {
        if ($kajian) {
            if ($kajian->status == LitbangKajianPengajuan::STATUS_PROCESS) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Kajian dengan status ' . LitbangKajianPengajuan::STATUS_PROCESS . ' tidak dapat diubah');
            }
        }

        $rawBody['submit_status'] = (array_key_exists('submit_status', $rawBody) ? $rawBody['submit_status'] : LitbangKajianPengajuan::STATUS_DRAFT);
        $params = $this->validate(
            $rawBody,
            [
                "krs_tgl_mulai" => ['validators' => ['required']],
                "krs_tgl_selesai" => ['validators' => ['required']],
                "kom_tgl_mulai" => ['validators' => ['required']],
                "kom_tgl_selesai" => ['validators' => ['required']],
                "pk_tgl_mulai" => ['validators' => ['required']],
                "pk_tgl_selesai" => ['validators' => ['required']],
                "kl_tgl_mulai" => ['validators' => ['required']],
                "kl_tgl_selesai" => ['validators' => ['required']],
                "rd_tgl_mulai" => ['validators' => ['required']],
                "rd_tgl_selesai" => ['validators' => ['required']],
                "pap_tgl_mulai" => ['validators' => ['required']],
                "pap_tgl_selesai" => ['validators' => ['required']],
                "rkk_instansi_id" => ['validators' => ['required', 'digit']],
                "rkk_studi_pendahuluan" => ['validators' => ['required']],
                "rkk_masalah_hipotesa" => ['validators' => ['required']],
                "rkk_pengumpulan_data_sekunder" => ['validators' => ['required']],
                "rkk_studi_literatur" => ['validators' => ['required']],
                "rkk_lingkup_kajian" => ['validators' => ['required']],
                "rkk_assign_to" => ['validators' => ['required', 'digit']],
                "submit_status" => ['validators' => ['required']],
                "attachments" => ['validators' => []],
            ]
        );
        $this->validateJenisDokumen(LitbangKajianPengajuan::TAHAPAN_RKK, $params['attachments']);

        $params['rkk_created_by'] = $this->userID;
        $params['status'] = $params['submit_status'] == LitbangKajianPengajuan::STATUS_PROCESS ? LitbangKajianPengajuan::STATUS_PROCESS : LitbangKajianPengajuan::STATUS_DRAFT;
        $params['tahapan'] = LitbangKajianPengajuan::TAHAPAN_RKK;

        return $params;
    }

    private function validateKrs($rawBody, $kajian = null)
    {
        if ($kajian) {
            if ($kajian->status == LitbangKajianPengajuan::STATUS_PROCESS) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Kajian dengan status ' . LitbangKajianPengajuan::STATUS_PROCESS . ' tidak dapat diubah');
            }
        }

        $rawBody['submit_status'] = (array_key_exists('submit_status', $rawBody) ? $rawBody['submit_status'] : LitbangKajianPengajuan::STATUS_DRAFT);
        $params = $this->validate(
            $rawBody,
            [
                "krs_tgl_realisasi" => ['validators' => ['required']],
                "krs_deskripsi" => ['validators' => ['required']],
                "krs_checklist" => ['validators' => ['required']],
                "krs_assign_to" => ['validators' => ['required', 'digit']],
                "submit_status" => ['validators' => ['required']],
                "attachments" => ['validators' => []],
            ]
        );
        $this->validateJenisDokumen(LitbangKajianPengajuan::TAHAPAN_KRS, $params['attachments']);
        if (!$this->validateJson($params['krs_checklist'])) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Format krs_checklist tidak valid!");
        }
        $params['krs_checklist'] = json_encode($params['krs_checklist']);

        $params['krs_created_by'] = $this->userID;
        $params['status'] = $params['submit_status'] == LitbangKajianPengajuan::STATUS_PROCESS ? LitbangKajianPengajuan::STATUS_PROCESS : LitbangKajianPengajuan::STATUS_DRAFT;
        $params['tahapan'] = LitbangKajianPengajuan::TAHAPAN_KRS;

        return $params;
    }

    private function validateKom($rawBody, $kajian = null)
    {
        if ($kajian) {
            if ($kajian->status == LitbangKajianPengajuan::STATUS_PROCESS) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Kajian dengan status ' . LitbangKajianPengajuan::STATUS_PROCESS . ' tidak dapat diubah');
            }
        }

        $rawBody['submit_status'] = (array_key_exists('submit_status', $rawBody) ? $rawBody['submit_status'] : LitbangKajianPengajuan::STATUS_DRAFT);
        $params = $this->validate(
            $rawBody,
            [
                "kom_tgl_realisasi" => ['validators' => ['required']],
                "kom_notulensi" => ['validators' => ['required']],
                "kom_assign_to" => ['validators' => ['required', 'digit']],
                "submit_status" => ['validators' => ['required']],
                "attachments" => ['validators' => []],
            ]
        );
        $this->validateJenisDokumen(LitbangKajianPengajuan::TAHAPAN_KOM, $params['attachments']);

        $params['kom_created_by'] = $this->userID;
        $params['status'] = $params['submit_status'] == LitbangKajianPengajuan::STATUS_PROCESS ? LitbangKajianPengajuan::STATUS_PROCESS : LitbangKajianPengajuan::STATUS_DRAFT;
        $params['tahapan'] = LitbangKajianPengajuan::TAHAPAN_KOM;

        return $params;
    }

    private function validatePk($rawBody, $kajian = null)
    {
        if ($kajian) {
            if ($kajian->status == LitbangKajianPengajuan::STATUS_PROCESS) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Kajian dengan status ' . LitbangKajianPengajuan::STATUS_PROCESS . ' tidak dapat diubah');
            }
        }

        $rawBody['submit_status'] = (array_key_exists('submit_status', $rawBody) ? $rawBody['submit_status'] : LitbangKajianPengajuan::STATUS_DRAFT);
        $params = $this->validate(
            $rawBody,
            [
                "pk_tgl_realisasi" => ['validators' => ['required']],
                "pk_is_pihak_ketiga" => ['validators' => ['required']],
                "pk_pihak_ketiga" => ['validators' => []],
                "pk_assign_to" => ['validators' => ['required', 'digit']],
                "submit_status" => ['validators' => ['required']],
                "attachments" => ['validators' => []],
            ]
        );
        $this->validateJenisDokumen(LitbangKajianPengajuan::TAHAPAN_PK, $params['attachments']);

        $params['pk_created_by'] = $this->userID;
        $params['status'] = $params['submit_status'] == LitbangKajianPengajuan::STATUS_PROCESS ? LitbangKajianPengajuan::STATUS_PROCESS : LitbangKajianPengajuan::STATUS_DRAFT;
        $params['tahapan'] = LitbangKajianPengajuan::TAHAPAN_PK;

        return $params;
    }

    private function validateKl($rawBody, $kajian = null)
    {
        if ($kajian) {
            if ($kajian->status == LitbangKajianPengajuan::STATUS_PROCESS) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Kajian dengan status ' . LitbangKajianPengajuan::STATUS_PROCESS . ' tidak dapat diubah');
            }
        }

        $rawBody['submit_status'] = (array_key_exists('submit_status', $rawBody) ? $rawBody['submit_status'] : LitbangKajianPengajuan::STATUS_DRAFT);
        $params = $this->validate(
            $rawBody,
            [
                "kl_tgl_realisasi" => ['validators' => ['required']],
                "kl_perumusan_masalah" => ['validators' => ['required']],
                "kl_metode_penelitian" => ['validators' => ['required']],
                "kl_tujuan_penelitian" => ['validators' => ['required']],
                "kl_kerangka_teori" => ['validators' => ['required']],
                "kl_gambaran_umum" => ['validators' => ['required']],
                "kl_teknis_penulisan" => ['validators' => ['required']],
                "kl_assign_to" => ['validators' => ['required', 'digit']],
                "submit_status" => ['validators' => ['required']],
                "attachments" => ['validators' => []],
            ]
        );
        $this->validateJenisDokumen(LitbangKajianPengajuan::TAHAPAN_KL, $params['attachments']);

        $params['kl_created_by'] = $this->userID;
        $params['status'] = $params['submit_status'] == LitbangKajianPengajuan::STATUS_PROCESS ? LitbangKajianPengajuan::STATUS_PROCESS : LitbangKajianPengajuan::STATUS_DRAFT;
        $params['tahapan'] = LitbangKajianPengajuan::TAHAPAN_KL;

        return $params;
    }

    private function validateRd($rawBody, $kajian = null)
    {
        if ($kajian) {
            if ($kajian->status == LitbangKajianPengajuan::STATUS_PROCESS) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Kajian dengan status ' . LitbangKajianPengajuan::STATUS_PROCESS . ' tidak dapat diubah');
            }
        }

        $rawBody['submit_status'] = (array_key_exists('submit_status', $rawBody) ? $rawBody['submit_status'] : LitbangKajianPengajuan::STATUS_DRAFT);
        $params = $this->validate(
            $rawBody,
            [
                "rd_tgl_realisasi" => ['validators' => ['required']],
                "rd_assign_to" => ['validators' => ['required', 'digit']],
                "submit_status" => ['validators' => ['required']],
                "attachments" => ['validators' => []],
            ]
        );
        $this->validateJenisDokumen(LitbangKajianPengajuan::TAHAPAN_RD, $params['attachments']);

        $params['rd_created_by'] = $this->userID;
        $params['status'] = $params['submit_status'] == LitbangKajianPengajuan::STATUS_PROCESS ? LitbangKajianPengajuan::STATUS_PROCESS : LitbangKajianPengajuan::STATUS_DRAFT;
        $params['tahapan'] = LitbangKajianPengajuan::TAHAPAN_RD;

        return $params;
    }

    private function validatePap($rawBody, $kajian = null)
    {
        if ($kajian) {
            if ($kajian->status == LitbangKajianPengajuan::STATUS_PROCESS) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Kajian dengan status ' . LitbangKajianPengajuan::STATUS_PROCESS . ' tidak dapat diubah');
            }
        }

        $rawBody['submit_status'] = (array_key_exists('submit_status', $rawBody) ? $rawBody['submit_status'] : LitbangKajianPengajuan::STATUS_DRAFT);
        $params = $this->validate(
            $rawBody,
            [
                "pap_tgl_realisasi" => ['validators' => ['required']],
                "pap_tgl_internal_deputi" => ['validators' => []],
                "pap_tgl_internal_direktur" => ['validators' => []],
                "pap_tgl_internal_eksternal" => ['validators' => []],
                "pap_is_diseminasi" => ['validators' => []],
                "pap_diseminasi" => ['validators' => []],
                "pap_assign_to" => ['validators' => ['required', 'digit']],
                "submit_status" => ['validators' => ['required']],
                "attachments" => ['validators' => []],
            ]
        );
        $this->validateJenisDokumen(LitbangKajianPengajuan::TAHAPAN_PAP, $params['attachments']);

        $params['pap_created_by'] = $this->userID;
        $params['status'] = $params['submit_status'] == LitbangKajianPengajuan::STATUS_PROCESS ? LitbangKajianPengajuan::STATUS_PROCESS : LitbangKajianPengajuan::STATUS_DRAFT;
        $params['tahapan'] = LitbangKajianPengajuan::TAHAPAN_PAP;

        return $params;
    }

    private function validateJenisDokumen($tahapan, $attachments) {
        $jenisDokumens = LitbangKajianPengajuanJenisDokumen::findByTahapan($tahapan)->toArray();
        $jenisDokumenIds = array_column($jenisDokumens, 'id');
        if ($attachments) {
            foreach ($attachments as $attachment) {
                $this->validate(
                    $attachment,
                    [
                        "attachment_id" => ['validators' => ['required', 'digit']],
                        "jenis_dokumen_id" => ['validators' => ['required', 'digit']],
                    ]
                );
                if (!in_array($attachment['jenis_dokumen_id'], $jenisDokumenIds)) {
                    throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Jenis dokumen tidak valid!");
                }
            }
        }
    }

    private function validateAndExtractTimeline($timeline) {
        try {
            $tgl_mulai = 4102358400;$tgl_selesai = 0;
            $decoded = json_decode($timeline, true);
            foreach ($decoded as $row) {
                if (!array_key_exists('nama_kegiatan', $row) || !array_key_exists('tgl_mulai', $row) || !array_key_exists('tgl_selesai', $row)) {
                    throw new \Exception();
                }
                $mulai = strtotime($row['tgl_mulai']);
                $selesai = strtotime($row['tgl_selesai']);
                $tgl_mulai = $mulai < $tgl_mulai ? $mulai : $tgl_mulai;
                $tgl_selesai = $selesai > $tgl_selesai ? $selesai : $tgl_selesai;
                if (array_key_exists('sub_kegiatan', $row)) {
                    foreach ($row['sub_kegiatan'] as $subRow) {
                        if (!array_key_exists('nama_kegiatan', $subRow) || !array_key_exists('tgl_mulai', $subRow) || !array_key_exists('tgl_selesai', $subRow)) {
                            throw new \Exception();
                        }
                    }
                }
            }

            return [
                'tgl_mulai' => date('Y-m-d', $tgl_mulai),
                'tgl_selesai' => date('Y-m-d', $tgl_selesai)
            ];
        } catch (\Exception $e) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Format timeline tidak valid!");
        }
    }

    private function validateJson($string) {
        json_encode($string);
        return json_last_error() === JSON_ERROR_NONE;
    }
}
