<?php

use chillerlan\QRCode\QRCode;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

class PancekFormulirController extends BaseController
{
    public function list()
    {
        $this->checkOneOfScopes(['pancek.admin', 'pancek.monitoring', 'pancek.verify', 'pancek.answer', 'pancek.kppbc.verify', 'pancek.kppbc.assignor', 'pancek.bk.verify']);
        PancekUserAccess::checkEligibleForRoleObserver($this);
        $rawParams = $this->request->get();
        if ($this->hasScopes(['pancek.answer'])) {
            $pancekUser = PancekUser::findByUmId($this->shared->user->um_id);
            $rawParams['instansi_id'] = $pancekUser['instansi']['id'];
        }
        $params = $this->validate(
            $rawParams,
            [
                'instansi_id' => ['validators' => ['digit', 'required']]
            ]
        );


        $idInstansiBeaCukai = Instansi::getIdInstansiDitjenBeaCukai();
        $idInstansiUser = $this->shared->user->id_instansi;

        if(
            ($this->hasScopes(['pancek.kppbc.verify']) && $idInstansiBeaCukai != $idInstansiUser) ||
            ($this->hasScopes(['pancek.kppbc.verify']) && !PancekInstansiVerifikator::findInstansiByIdAndAccess($params['instansi_id'], $this->userID, $this))
        ) {
            throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, 'User is not allowed to perform this action');
        }
        if($this->hasScopes(['pancek.kppbc.assignor']) && $idInstansiBeaCukai != $idInstansiUser) {
            throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, 'User is not allowed to perform this action');
        }
        if($this->hasScopes(['pancek.bk.verify']) && !PancekInstansiVerifikator::findInstansiByIdAndAccess($params['instansi_id'], $this->userID, $this)) {
            throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, 'User is not allowed to perform this action');
        }

        $conditions = 'instansi_id = :instansi_id: AND deleted_at IS NULL';
        $bind = ['instansi_id' => $params['instansi_id']];

        /*if($this->hasScopes(['pancek.kppbc.verify'])) {
            $conditions  .= ' AND kppbc_um_id = :um_id:';
            $bind['um_id'] = $this->userID;
        }*/

        if ($this->hasOneOfScopes(['pancek.verify', 'pancek.kppbc.verify', 'pancek.bk.verify'])) {
            $conditions .= ' AND status NOT IN ({not_reviewed_statuses:array})';
            $bind['not_reviewed_statuses'] = [PancekFormulir::STATUS_NEW, PancekFormulir::STATUS_DRAFT];
        }

        $rows = PancekFormulir::find([
            'conditions' => $conditions,
            'bind' => $bind,
            'order' => 'id desc'
        ]);
        $result = [];
        foreach ($rows as $row) {
            /** @var $row PancekFormulir */
            $res = $row->toArray();
            $res['is_active'] = !$row->isExpired();
            $res['answered'] = $row->countAnswered();
            $res['answer'] = $row->countAnswer();
            $res['is_download_available'] = $row->isDownloadAvailable();
            $res['is_download_verified_available'] = $row->isDownloadVerifiedAvailable();
            array_push($result, $res);
        }
        return new CollectionPaginatedResponse($result);
    }

    public function listAdmin()
    {
        $this->checkOneOfScopes(['pancek.admin', 'pancek.monitoring', 'pancek.verify']);
        PancekUserAccess::checkEligibleForRoleObserver($this);
        $rawParams = $this->request->get();
        $rawParams['limit'] = array_key_exists("limit", $rawParams) ? $rawParams['limit'] : 10;
        $rawParams['offset'] = array_key_exists("offset", $rawParams) ? $rawParams['offset'] : 0;
        $rawParams['instansi_id'] = array_key_exists("instansi_id", $rawParams) ? $rawParams['instansi_id'] : null;
        $rawParams['status'] = array_key_exists("status", $rawParams) ? $rawParams['status'] : null;
        $rawParams['keyword'] = array_key_exists("keyword", $rawParams) ? $rawParams['keyword'] : null;
        $params = $this->validate(
            $rawParams,
            [
                'limit' => ['validators' => ['digit', 'required']],
                'offset' => ['validators' => ['digit', 'required']],
                'instansi_id' => ['validators' => ['digit']],
                'keyword' => ['validators' => [
                    [
                        'name' => 'max_string',
                        'params' => 100
                    ]
                ]]
            ]
        );
        if (!is_null($rawParams['status'])) {
            $this->validate(
                $rawParams,
                [
                    'status' => ['validators' => [
                        [
                            'name' => 'inclusion_in',
                            'params' => [PancekFormulir::STATUS_NEED_TO_REVIEW, PancekFormulir::STATUS_VERIFIED, PancekFormulir::STATUS_REVIEWED, PancekFormulir::STATUS_DRAFT, PancekFormulir::STATUS_NEW]
                        ]
                    ]],
                ]
            );
        }
        $params['status'] = $rawParams['status'];

        $query = "
            WHERE
            f.deleted_at IS NULL 
            AND now() <= ( f.created_at + INTERVAL '".getenv("PANCEK_FORMULIR_VALIDITY_PERIOD")." year' )
        ";
        $bind = [];
        if (!is_null($params['status'])) {
            $query .= " AND f.status LIKE :status";
            $bind['status'] = $params['status'];
        }
        if (!is_null($params['instansi_id'])) {
            $query .= " AND f.instansi_id = :instansi_id";
            $bind['instansi_id'] = $params['instansi_id'];
        }
        if (!is_null($params['keyword'])) {
            $query .= " AND (i.nama ILIKE :keyword OR i.npwp ILIKE :keyword OR i.nomor ILIKE :keyword)";
            $bind['keyword'] = "%" . $params['keyword'] . "%";
        }
        $rows = $this->rawQuery("SELECT f.*, i.nama as instansi_nama FROM jaga.pancek_formulir f LEFT JOIN jaga.pancek_instansi i ON i.id = f.instansi_id " . $query . " order by f.updated_at DESC limit ".$params['limit']." offset ".$params['offset'], $bind);
        $total = $this->rawQuery("SELECT COUNT(*) as total from jaga.pancek_formulir f LEFT JOIN jaga.pancek_instansi i ON i.id = f.instansi_id " . $query, $bind)[0]['total'];

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

    public function listPublic()
    {
        $rawParams = $this->request->get();
        $rawParams['limit'] = array_key_exists("limit", $rawParams) ? $rawParams['limit'] : 10;
        $rawParams['offset'] = array_key_exists("offset", $rawParams) ? $rawParams['offset'] : 0;
        $rawParams['verified'] = array_key_exists("verified", $rawParams) ? $rawParams['verified'] : 0;
        $rawParams['verified_by'] = array_key_exists("verified_by", $rawParams) ? $rawParams['verified_by'] : 'kpk';
        $rawParams['keyword'] = array_key_exists("keyword", $rawParams) ? $rawParams['keyword'] : null;
        $params = $this->validate(
            $rawParams,
            [
                'limit' => ['validators' => ['digit', 'required']],
                'offset' => ['validators' => ['digit', 'required']],
                'verified' => ['validators' => ['digit', 'required']],
                'verified_by' => ['validators' => [[
                    'name' => 'inclusion_in',
                    'params' => ['kpk', 'beacukai', 'pupr']
                ]]],
                'jenis_instansi_id' => ['validators' => ['digit']],
                'sektor_usaha_id' => ['validators' => ['digit']],
                'provinsi_id' => ['validators' => ['digit']],
                'keyword' => ['validators' => [
                    [
                        'name' => 'max_string',
                        'params' => 50
                    ]
                ]]
            ]
        );

        $idInstansiBeaCukai = Instansi::getIdInstansiDitjenBeaCukai();
        $idInstansiPupr = Instansi::getIdInstansiDitjenPupr();

        $wheres = [];
        $binds = [];
        $bindsWithoutLimitOffset = [];

        if ($params['verified'] == 0) {
            $wheres[] = "v.is_verified = false";
        } else {
            $wheres[] = "v.is_verified = true";

            if ($params['verified_by'] == 'beacukai') {
                $wheres[] = "u.id_instansi = " . $idInstansiBeaCukai;
            } elseif ($params['verified_by'] == 'pupr') {
                $wheres[] = "u.id_instansi = " . $idInstansiPupr;
            } else {
                $wheres[] = "u.id_instansi NOT IN ( " . $idInstansiBeaCukai . ", ". $idInstansiPupr . ")";
            }
        }
        if (!empty($params['keyword'])) {
            $wheres[] = "pi.nama ILIKE :keyword";
            $binds['keyword'] = "%" . $params['keyword'] . "%";
            $bindsWithoutLimitOffset['keyword'] = "%" . $params['keyword'] . "%";
        }
        if (!empty($params['jenis_instansi_id'])) {
            $wheres[] = "pi.jenis_instansi_id = :jenis_instansi_id" ;
            $binds['jenis_instansi_id'] = $params['jenis_instansi_id'];
            $bindsWithoutLimitOffset['jenis_instansi_id'] = $params['jenis_instansi_id'];
        }
        if (!empty($params['sektor_usaha_id'])) {
            $wheres[] = "pi.sektor_usaha_id = :sektor_usaha_id";
            $binds['sektor_usaha_id'] = $params['sektor_usaha_id'];
            $bindsWithoutLimitOffset['sektor_usaha_id'] = $params['sektor_usaha_id'];
        }
        if (!empty($params['provinsi_id'])) {
            $wheres[] = "pi.provinsi_id = :provinsi_id";
            $binds['provinsi_id'] = $params['provinsi_id'];
            $bindsWithoutLimitOffset['provinsi_id'] = $params['provinsi_id'];
        }

        $binds['limit'] = (int)$params["limit"];
        $binds['offset'] = (int)$params["offset"];

        $q = "
            WITH KSP_CTE AS (
                SELECT pf.id AS id_formulir, ksp.id AS id_ksp, COUNT(CASE WHEN pa.verify_status = 'ACCEPTED' THEN 1 END) AS verified_count, COUNT(*) AS total_count
                FROM pancek_answer pa
                LEFT JOIN pancek_komponen_indikator pki ON pa.komponen_indikator_id = pki.id
                LEFT JOIN pancek_indikator pi ON pki.indikator_id = pi.id
                LEFT JOIN pancek_konsep_sistem_pencegahan ksp ON pi.konsep_sistem_pencegahan_id = ksp.id
                JOIN pancek_formulir pf ON pa.formulir_id = pf.id
                WHERE pf.deleted_at IS NULL
                GROUP BY pf.id, ksp.id
                ORDER BY pf.id, ksp.id
            ),
            AVG_KSP AS (
            SELECT 
                id_formulir,
                id_ksp,
                AVG(verified_count::FLOAT / NULLIF(total_count, 0)) AS avg_verified
            FROM KSP_CTE
            GROUP BY id_formulir, id_ksp
            )
            SELECT 
                f.id, v.is_verified, pi.nama as badan_usaha, ji.nama as jenis_instansi, su.nama as sektor_usaha, f.status, (CASE WHEN f.status = 'VERIFIED' THEN f.verified_at WHEN f.status = 'REVIEWED' THEN f.reviewed_at END) as verified_at, u.nama as verified_by, i.nama as verified_instansi, mp.provinsi_name as provinsi, mkk.kota_kabupaten_name as kab_kota
            FROM (
              SELECT id_formulir, BOOL_AND(avg_verified >= ".(PancekFormulir::MINIMUM_PERCENTAGE_TO_DOWNLOAD/100).") AS is_verified FROM AVG_KSP GROUP BY id_formulir
            ) v
            LEFT JOIN pancek_formulir f on f.id = v.id_formulir
            LEFT JOIN um_user u ON (CASE WHEN f.status = 'VERIFIED' THEN u.um_id = f.verified_by WHEN f.status = 'REVIEWED' THEN u.um_id = f.reviewed_by ELSE u.um_id = 0 END)
            LEFT JOIN instansi i ON i.id = u.id_instansi
            LEFT JOIN pancek_instansi pi ON pi.id = f.instansi_id
            LEFT JOIN pancek_sektor_usaha su ON su.id = pi.sektor_usaha_id
            LEFT JOIN pancek_jenis_instansi ji ON ji.id = pi.jenis_instansi_id
            LEFT JOIN master_provinsi mp ON mp.provinsi_code::INTEGER = pi.provinsi_id
            LEFT JOIN master_kota_kabupaten mkk ON mkk.kota_kabupaten_code::INTEGER = pi.kab_kota_id
            WHERE ".implode(" AND ", $wheres)."
            ORDER BY pi.nama
            LIMIT :limit OFFSET :offset
        ";
        // $rows = $this->rawQuery($q, ["limit" => $params["limit"], "offset" => $params["offset"]]);
        $rows = $this->rawQuery($q, $binds);

        $total = $this->rawQuery("
            WITH KSP_CTE AS (
                SELECT pf.id AS id_formulir, ksp.id AS id_ksp, COUNT(CASE WHEN pa.verify_status = 'ACCEPTED' THEN 1 END) AS verified_count, COUNT(*) AS total_count
                FROM pancek_answer pa
                LEFT JOIN pancek_komponen_indikator pki ON pa.komponen_indikator_id = pki.id
                LEFT JOIN pancek_indikator pi ON pki.indikator_id = pi.id
                LEFT JOIN pancek_konsep_sistem_pencegahan ksp ON pi.konsep_sistem_pencegahan_id = ksp.id
                JOIN pancek_formulir pf ON pa.formulir_id = pf.id
                WHERE pf.deleted_at IS NULL
                GROUP BY pf.id, ksp.id
                ORDER BY pf.id, ksp.id
            ),
            AVG_KSP AS (
            SELECT 
                id_formulir,
                id_ksp,
                AVG(verified_count::FLOAT / NULLIF(total_count, 0)) AS avg_verified
            FROM KSP_CTE
            GROUP BY id_formulir, id_ksp
            )
            SELECT 
                count(f.id) as total
            FROM (
              SELECT id_formulir, BOOL_AND(avg_verified >= ".(PancekFormulir::MINIMUM_PERCENTAGE_TO_DOWNLOAD/100).") AS is_verified FROM AVG_KSP GROUP BY id_formulir
            ) v
            LEFT JOIN pancek_formulir f on f.id = v.id_formulir
            LEFT JOIN um_user u ON u.um_id = f.verified_by
            LEFT JOIN instansi i ON i.id = u.id_instansi
            LEFT JOIN pancek_instansi pi ON pi.id = f.instansi_id
            LEFT JOIN pancek_sektor_usaha su ON su.id = pi.sektor_usaha_id
            LEFT JOIN pancek_jenis_instansi ji ON ji.id = pi.jenis_instansi_id
            LEFT JOIN master_provinsi mp ON mp.provinsi_code::INTEGER = pi.provinsi_id
            LEFT JOIN master_kota_kabupaten mkk ON mkk.kota_kabupaten_code::INTEGER = pi.kab_kota_id
            WHERE ".implode(" AND ", $wheres), $bindsWithoutLimitOffset
        )[0]['total'];

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

    public function create()
    {
        $this->checkOneOfScopes(['pancek.admin', 'pancek.answer']);
        $rawParams = $this->request->getJsonRawBody(true);
        if ($this->hasScopes(['pancek.answer'])) {
            $pancekUser = PancekUser::findByUmId($this->shared->user->um_id);
            $rawParams['instansi_id'] = $pancekUser['instansi']['id'];
        }
        $params = $this->validate(
            $rawParams,
            [
                'instansi_id' => ['validators' => ['digit', 'required']]
            ]
        );

        $formulirExist = $this->rawQuery(
            "SELECT COUNT(*) as total FROM jaga.pancek_formulir WHERE instansi_id = :instansi_id AND deleted_at IS NULL AND now() <= (created_at + interval '".getenv("PANCEK_FORMULIR_VALIDITY_PERIOD")." year')",
            [
                'instansi_id' => $params['instansi_id']
            ]
        )[0]['total'];

        if ($formulirExist > 0) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Tidak dapat membuat formulir baru. Terdapat formulir yang masih aktif");
        }

        /** @var $instansiExist PancekInstansi|null */
        $instansiExist = PancekInstansi::findFirst($params['instansi_id']);

        if (!$instansiExist) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Instansi tidak ditemukan");
        }
        if (!$instansiExist->jenisInstansi) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Jenis instansi tidak valid");
        }

        $komponenIndikators = PancekKomponenIndikator::find([
            'conditions' => 'deleted_at IS NULL AND is_header = FALSE AND formulir_type = :type:',
            'order' => 'id asc',
            'bind' => ['type' => $instansiExist->jenisInstansi->formulir_type]
        ]);

        try{
            $this->db->begin();
            $formulir = new PancekFormulir();
            $formulir->instansi_id = $params['instansi_id'];
            $formulir->periode = date('Y');
            $formulir->status = PancekFormulir::STATUS_NEW;
            if (!$formulir->save()) {
                $errors = $formulir->getMessages();
                $error_messages = [];

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

                throw new ValidationException($error_messages);
            }
            foreach ($komponenIndikators as $komponenIndikator) {
                /** @var $komponenIndikator PancekKomponenIndikator */
                $answer = new PancekAnswer();
                $answer->formulir_id = $formulir->id;
                $answer->komponen_indikator_id = $komponenIndikator->id;
                if (!$answer->save()) {
                    $errors = $answer->getMessages();
                    $error_messages = [];

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

                    throw new ValidationException($error_messages);
                }
            }
            /** @var $formulir PancekFormulir */
            $formulir = PancekFormulir::findFirst($formulir->id);
            $res = $formulir->getForm();

            $formulirExist = $this->rawQuery(
                "SELECT COUNT(*) as total FROM jaga.pancek_formulir WHERE instansi_id = :instansi_id AND deleted_at IS NULL AND now() <= (created_at + interval '".getenv("PANCEK_FORMULIR_VALIDITY_PERIOD")." year')",
                [
                    'instansi_id' => $params['instansi_id']
                ]
            )[0]['total'];

            if ($formulirExist > 1) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Tidak dapat membuat formulir baru. Terdapat formulir yang masih aktif [2]");
            }

            $pancekService = new PancekService($this);
            $pancekService->logFormulirCreate($formulir);

            $this->db->commit();
            return new SimpleResponse($res);
        } catch (\Exception $e) {
            $this->db->rollback();
            throw $e;
        }
    }

    public function detail($id)
    {
        $this->checkOneOfScopes(['pancek.admin', 'pancek.monitoring', 'pancek.verify', 'pancek.answer', 'pancek.kppbc.verify', 'pancek.kppbc.assignor', 'pancek.bk.verify']);
        PancekUserAccess::checkEligibleForRoleObserver($this);
        /** @var $formulir PancekFormulir */
        $formulir = PancekFormulir::findFirst($id);
        if (!$formulir) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Formulir tidak ditemukan");
        }
        if ($this->hasScopes(['pancek.answer']) && !$this->hasOneOfScopes(['pancek.admin', 'pancek.monitoring', 'pancek.verify', 'pancek.kppbc.verify', 'pancek.kppbc.assignor'])) {
            /** @var $pancekUser PancekUser */
            $pancekUser = PancekUser::findByUmId($this->shared->user->um_id);
            if ($formulir->instansi_id != $pancekUser['instansi_id']) {
                throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, "Anda tidak berhak mengakses");
            }
        }

        $idInstansiBeaCukai = Instansi::getIdInstansiDitjenBeaCukai();
        $idInstansiUser = $this->shared->user->id_instansi;
        if($this->hasScopes(['pancek.kppbc.verify']) && $idInstansiBeaCukai != $idInstansiUser) {
            throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, 'User is not allowed to perform this action');
        }
        if($this->hasScopes(['pancek.kppbc.assignor']) && $idInstansiBeaCukai != $idInstansiUser) {
            throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, 'User is not allowed to perform this action');
        }
        if($this->hasScopes(['pancek.kppbc.verify'])) {
            if (!PancekInstansiVerifikator::findInstansiByIdAndAccess($formulir->instansi_id, $this->userID, $this)) {
                throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, 'User is not allowed to perform this action');
            }
            /*if($formulir->kppbc_um_id != $this->userID) {
                throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, 'User is not allowed to perform this action');
            }*/
        }
        if($this->hasScopes(['pancek.bk.verify'])) {
            if (!PancekInstansiVerifikator::findInstansiByIdAndAccess($formulir->instansi_id, $this->shared->user->um_id, $this)) {
                throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, 'User is not allowed to perform this action');
            }
        }

        return new SimpleResponse($formulir->getForm());
    }

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

        $rawParams = $this->request->getJsonRawBody(true);
        $validatedParams = $this->validate($rawParams, [
            'status' => [
                'validators' => [
                    'required',
                    [
                        'name' => 'inclusion_in',
                        'params' => [PancekFormulir::STATUS_NEW, PancekFormulir::STATUS_DRAFT, PancekFormulir::STATUS_NEED_TO_REVIEW, PancekFormulir::STATUS_REVIEWED, PancekFormulir::STATUS_VERIFIED]
                    ]
                ]
            ],
            'jumlah_revisi' => [
                'validators' => [
                    'required',
                    'digit',
                    [
                        'name' => 'min',
                        'params' => 0
                    ],
                    [
                        'name' => 'max',
                        'params' => getenv("PANCEK_FORMULIR_MAX_REVISION")
                    ]
                ]
            ]
        ]);

        /** @var $formulir PancekFormulir */
        $formulir = PancekFormulir::findFirst($id);
        if (!$formulir) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Formulir tidak ditemukan");
        }

        $formulir->status = $validatedParams['status'];
        $formulir->jumlah_revisi = $validatedParams['jumlah_revisi'];
        if (!$formulir->save()) {
            $this->throwObjectValidationException($formulir);
        }

        return new SimpleResponse($formulir);
    }

    public function delete($id)
    {
        $this->checkScopes(['pancek.admin']);
        $formulir = PancekFormulir::findFirst($id);
        if (!$formulir || $formulir->deleted_at != null) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Formulir tidak ditemukan atau telah dihapus");
        }

        try {
            $this->db->begin();

            $now = date('Y-m-d H:i:s');
            $this->rawQuery("UPDATE jaga.pancek_answer SET deleted_at = :now WHERE formulir_id = :id", ['id' => $id, 'now' => $now]);
            $formulir->deleted_at = $now;
            if (!$formulir->save()) {
                throw new CustomErrorException(BaseResponse::UNEXPECTED_ERROR, "Fomulir tidak dapat dihapus.");
            }
            $this->db->commit();
            return new SimpleResponse($formulir->id);
        } catch (\Exception $e) {
            $this->db->rollback();
            throw new CustomErrorException(BaseResponse::UNEXPECTED_ERROR, $e->getMessage());
        }
    }

    public function restore($id)
    {
        $this->checkScopes(['pancek.admin']);
        $formulir = PancekFormulir::findFirst($id);
        if (!$formulir || $formulir->deleted_at == null) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Formulir tidak ditemukan atau telah aktif");
        }

        $activeFormulir = PancekFormulir::findActiveByInstansiId($formulir->instansi_id);
        if ($activeFormulir) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Formulir tidak dapat diaktifkan. Masih terdapat formulir lain yang telah aktif.");
        }

        try {
            $this->db->begin();

            $this->rawQuery("UPDATE jaga.pancek_answer SET deleted_at = NULL WHERE formulir_id = :id", ['id' => $id]);
            $formulir->deleted_at = NULL;
            if (!$formulir->save()) {
                throw new CustomErrorException(BaseResponse::UNEXPECTED_ERROR, "Fomulir tidak dapat diaktifkan.");
            }
            $this->db->commit();
            return new SimpleResponse($formulir->id);
        } catch (\Exception $e) {
            $this->db->rollback();
            throw new CustomErrorException(BaseResponse::UNEXPECTED_ERROR, $e->getMessage());
        }
    }

    public function answers($id)
    {
        $this->checkOneOfScopes(['pancek.admin', 'pancek.monitoring', 'pancek.verifiy']);
        PancekUserAccess::checkEligibleForRoleObserver($this);
        /** @var $formulir PancekFormulir */
        $formulir = PancekFormulir::findFirst($id);
        if (!$formulir) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Formulir tidak ditemukan");
        }

        $params = $this->request->get();
        $params['komponen_indikator_id'] = array_key_exists('komponen_indikator_id', $params) ? $params['komponen_indikator_id'] : null;
        $params['indikator_id'] = array_key_exists('indikator_id', $params) ? $params['indikator_id'] : null;
        $params['konsep_sistem_pencegahan_id'] = array_key_exists('konsep_sistem_pencegahan_id', $params) ? $params['konsep_sistem_pencegahan_id'] : null;
        $this->validate($params, [
            'komponen_indikator_id' => ['validators' => ['digit']],
            'indikator_id' => ['validators' => ['digit']],
            'konsep_sistem_pencegahan_id' => ['validators' => ['digit']],
        ]);

        $where = PancekAnswer::class.".formulir_id = :formulir_id:";
        $bind = ['formulir_id' => $id];
        if (!empty($params['komponen_indikator_id'])) {
            $where .= " AND ".PancekKomponenIndikator::class.".id = :komponen_indikator_id:";
            $bind['komponen_indikator_id'] = $params['komponen_indikator_id'];
        }
        if (!empty($params['indikator_id'])) {
            $where .= " AND ".PancekIndikator::class.".id = :indikator_id:";
            $bind['indikator_id'] = $params['indikator_id'];
        }
        if (!empty($params['konsep_sistem_pencegahan_id'])) {
            $where .= " AND ".PancekKonsepSistemPencegahan::class.".id = :konsep_sistem_pencegahan_id:";
            $bind['konsep_sistem_pencegahan_id'] = $params['konsep_sistem_pencegahan_id'];
        }

        $rows = PancekAnswer::query()
            ->columns(
                PancekAnswer::class.".id as answer_id,".
                PancekKomponenIndikator::class.".id as komponen_indikator_id,".
                PancekKomponenIndikator::class.".label as komponen_indikator_label,".
                PancekIndikator::class.".id as indikator_id,".
                PancekIndikator::class.".label as indikator_label,".
                PancekKonsepSistemPencegahan::class.".id as konsep_sistem_pencegahan_id,".
                PancekKonsepSistemPencegahan::class.".label as konsep_sistem_pencegahan_label"
            )
            ->leftJoin(PancekKomponenIndikator::class, PancekKomponenIndikator::class.".id = ".PancekAnswer::class.".komponen_indikator_id")
            ->leftJoin(PancekIndikator::class, PancekIndikator::class.".id = ".PancekKomponenIndikator::class.".indikator_id")
            ->leftJoin(PancekKonsepSistemPencegahan::class, PancekKonsepSistemPencegahan::class.".id = ".PancekIndikator::class.".konsep_sistem_pencegahan_id")
            ->where($where)
            ->orderBy(PancekKonsepSistemPencegahan::class.".id,".PancekIndikator::class.".id,".PancekKomponenIndikator::class.".id,".PancekAnswer::class.".id")
            ->bind($bind)
            ->execute();

        $result = [];
        foreach ($rows as $row) {
            $d = $row->toArray();
            $answer = new PancekAnswer();
            $answer->id = $d['answer_id'];
            $rawAttachments = $answer->attachments;
            $attachments = [];
            foreach ($rawAttachments as $rawAttachment) {
                array_push($attachments, $rawAttachment->getDetail());
            }
            $d['attachments'] = $attachments;
            array_push($result, $d);
        }

        return new SimpleResponse($result);
    }

    public function submit()
    {
        $this->checkOneOfScopes(['pancek.admin', 'pancek.verify', 'pancek.answer']);
        $rawParams = $this->request->getJsonRawBody(true);
        $params = $this->validate(
            $rawParams,
            [
                'formulir_id' => ['validators' => ['digit', 'required']],
                'payload' => ['validators' => []]
            ]
        );

        $pancekService = new PancekService($this);

        if (!is_array($params['payload'])) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Format payload tidak valid");
        }

        /** @var $formulir PancekFormulir */
        $formulir = PancekFormulir::findFirst($params['formulir_id']);
        if (!$formulir) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Formulir tidak ditemukan");
        }

        // user instansi mengubah status formulir menjadi:
        // DRAFT jika save partial
        // NEED_TO_REVIEW jika save all
        if ($this->hasScopes(['pancek.answer'])) {
            $this->validate($rawParams, [
                'status' => ['validators' => [
                    'required',
                    [
                        'name' => 'inclusion_in',
                        'params' => [PancekFormulir::STATUS_DRAFT, PancekFormulir::STATUS_NEED_TO_REVIEW]
                    ]
                ]]
            ]);
            $status = $rawParams['status'];
            $pancekUser = PancekUser::findByUmId($this->shared->user->um_id);
            $instansiId = $pancekUser['instansi']['id'];
            if ($formulir->instansi_id != $instansiId) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "User tidak dapat mengubah status Formulir ini");
            }
            if ($formulir->isExpired()) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Formulir telah expired");
            }
            if ($formulir->isExceedRevisionLimit()) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Formulir telah melebihi batas maksimum revisi");
            }
            if($formulir->status == PancekFormulir::STATUS_NEED_TO_REVIEW || $formulir->status == PancekFormulir::STATUS_REVIEW_IN_PROGRESS) {
                throw new CustomErrorException(BaseResponse::DATA_NOT_AVAILABLE, "Formulir sudah diteruskan dan sedang proses review");
            }
            if($formulir->status == PancekFormulir::STATUS_VERIFIED) {
                throw new CustomErrorException(BaseResponse::DATA_NOT_AVAILABLE, "Formulir sudah diverifikasi");
            }
            // setiap kali user instansi save all, counter revisi akan bertambah
            if ($status === PancekFormulir::STATUS_NEED_TO_REVIEW) {
                if (is_numeric($formulir->jumlah_revisi)) {
                    $formulir->jumlah_revisi++;
                } else {
                    $formulir->jumlah_revisi = 1;
                }
            }
        }
        // verifikator mengubahh status formulir menjadi
        // REVIEWED jika formulir perlu diperbaiki oleh user instansi
        // VERIFIED jika formulir sudah sesuai
        else {
            $this->validate($rawParams, [
                'status' => ['validators' => [
                    'required',
                    [
                        'name' => 'inclusion_in',
                        'params' => [PancekFormulir::STATUS_REVIEWED, PancekFormulir::STATUS_VERIFIED]
                    ]
                ]]
            ]);
            $status = $rawParams['status'];
            if ($status == PancekFormulir::STATUS_REVIEWED) {
                $formulir->reviewed_at = DateHelper::getNowTimezoned();
                $formulir->reviewed_by = $this->shared->user->um_id;
            } elseif($status == PancekFormulir::STATUS_VERIFIED) {
                $formulir->verified_at = DateHelper::getNowTimezoned();
                $formulir->verified_by = $this->shared->user->um_id;
            }
        }

        $formulir->updated_at = DateHelper::getNowTimezoned();
        $formulir->status = $status;
        try{
            $this->db->begin();
            if (!$formulir->save()) {
                $errors = $formulir->getMessages();
                $error_messages = [];

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

                throw new ValidationException($error_messages);
            }
            foreach ($params['payload'] as $payload) {
                if (!array_key_exists('answer_id', $payload) || !array_key_exists('attachments', $payload) || !is_array($payload['attachments'])) {
                    throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Format payload tidak valid");
                }

                /** @var $answer PancekAnswer */
                $answer = PancekAnswer::findFirst([
                    'conditions' => 'id = :id: AND formulir_id = :formulir_id:',
                    'bind' => [
                        'id' => $payload['answer_id'],
                        'formulir_id' => $params['formulir_id']
                    ]
                ]);
                if (!$answer) {
                    throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Item formulir tidak ditemukan");
                }

                if($answer->verify_status != PancekAnswer::VERIFY_STATUS_DISETUJUI) {
                    // user instansi hanya mengubah lampiran
                    if ($this->hasScopes(['pancek.answer'])) {
                        $this->processAnswer($answer, $payload['attachments']);
                        if ($formulir->status == PancekFormulir::STATUS_NEED_TO_REVIEW && $answer->verify_status == PancekAnswer::VERIFY_STATUS_REVISED) {
                            $pancekService->logAnswerSubmit($answer);
                        }
                    }
                    // verifikator dapat mengubah/hapus lampiran dan menambahkan keterangan
                    else {
                        $keterangan = array_key_exists('keterangan', $payload) ? $payload['keterangan'] : null;
                        $this->processAnswer($answer, $payload['attachments'], $keterangan);
                    }
                }
            }

            $pancekService->handleFormulirSubmitNotification($formulir);
            $pancekService->logFormulirSubmit($formulir);

            $this->db->commit();
            return new ObjectResponse(['success' => true]);
        } catch(\Exception $e) {
            $this->db->rollback();
            throw $e;
        }
    }

    public function verify($id) {
        $this->checkOneOfScopes(['pancek.admin', 'pancek.verify', 'pancek.kppbc.verify', 'pancek.bk.verify']);
        if($this->hasScopes(['pancek.kppbc.verify']) && !$this->hasOneOfScopes(['pancek.admin', 'pancek.verify'])) {
            $idInstansiBeaCukai = Instansi::getIdInstansiDitjenBeaCukai();
            if($this->shared->user->id_instansi != $idInstansiBeaCukai) {
                throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, 'User is not allowed to perform this action');
            }
        }

        /** @var PancekFormulir $formulir */
        $formulir = PancekFormulir::findFirst($id);
        if (!$formulir) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Formulir tidak ditemukan.");
        }
        if ($formulir->status != PancekFormulir::STATUS_NEED_TO_REVIEW && $formulir->status != PancekFormulir::STATUS_REVIEW_IN_PROGRESS) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Formulir status tidak valid.");
        }

        if($this->hasScopes(['pancek.kppbc.verify']) && !$this->hasOneOfScopes(['pancek.admin', 'pancek.verify'])) {
            if (!PancekInstansiVerifikator::findInstansiByIdAndAccess($formulir->instansi_id, $this->userID, $this)) {
                throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, 'Anda tidak ditugaskan pada Badan Usaha ini atau akses verifikasi Anda telah habis');
            }
            /*if($formulir->kppbc_um_id != $this->userID) {
                throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, 'User is not allowed to perform this action');
            }*/
        }

        $countAnswerAccepted = 0;
        $countAnswerRejected = 0;
        $countAnswerHaveNotReviewed = 0;
        $answersHaveNotReviewed = [];
        $answers = $formulir->answers;
        $countAnswer = count($answers);
        if($countAnswer == 0) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_AVAILABLE, "Formulir kosong");
        }
        foreach ($answers as $answer) {
            /** @var PancekAnswer $answer */
            if($answer->verify_status == PancekAnswer::VERIFY_STATUS_DISETUJUI) {
                $countAnswerAccepted++;
            }
            else if($answer->verify_status == PancekAnswer::VERIFY_STATUS_DIKEMBALIKAN) {
                $countAnswerRejected++;
            }
            else if($answer->verify_status == PancekAnswer::VERIFY_STATUS_UPDATED || $answer->verify_status == PancekAnswer::VERIFY_STATUS_REVISED) {
                $countAnswerHaveNotReviewed++;
                $answersHaveNotReviewed[] = $answer->komponenIndikator->code;
            }
        }

        if($countAnswerHaveNotReviewed > 0) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_AVAILABLE, "Ada dokumen yg belum diverifikasi: " . implode(", ", $answersHaveNotReviewed));
        }

        $now = DateHelper::getNowTimezoned();
        $logFormulir = new PancekFormulirLogVerifikasi();
        $logFormulir->um_id = $formulir->id;
        $logFormulir->formulir_id = $formulir->id;
        $logFormulir->sla_started_at = $formulir->kppbc_verify_start_date;
        $logFormulir->sla_ended_at = $formulir->kppbc_verify_end_date;
        $logFormulir->verified_at = $now;

        if($countAnswerRejected > 0 || $countAnswerAccepted < $countAnswer) {
            $formulir->reviewed_at = $now;
            $formulir->reviewed_by = $this->shared->user->um_id;
            $formulir->status = PancekFormulir::STATUS_REVIEWED;
            $logFormulir->verify_status = PancekFormulir::STATUS_REVIEWED;
        }
        else {
            $formulir->verified_at = $now;
            $formulir->verified_by = $this->shared->user->um_id;
            $formulir->status = PancekFormulir::STATUS_VERIFIED;
            $logFormulir->verify_status = PancekFormulir::STATUS_VERIFIED;
        }
        $logFormulir->um_id = $this->userID;
        $logFormulir->save();
        $formulir->kppbc_verify_start_date = null;
        $formulir->kppbc_verify_end_date = null;


        if (!$formulir->save()) {
            $errors = $formulir->getMessages();
            $error_messages = [];

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

            throw new ValidationException($error_messages);
        }

        $pancekService = new PancekService($this);
        $pancekService->handleFormulirVerifyNotification($formulir);
        $pancekService->logFormulirSubmit($formulir);

        return new SimpleResponse($formulir);
    }

    public function generateExcel($id) {
        $this->checkOneOfScopes(['pancek.admin', 'pancek.answer', 'pancek.verify', 'pancek.monitoring']);
        PancekUserAccess::checkEligibleForRoleObserver($this);
        /** @var $formulir PancekFormulir */
        $formulir = PancekFormulir::findFirst($id);

        if (!$formulir) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Formulir tidak ditemukan");
        }

        if (!$this->hasOneOfScopes(['pancek.admin', 'pancek.verify', 'pancek.monitoring'])) {
            if ($formulir->instansi->user->umUser->um_id != $this->shared->user->um_id) {
                throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, "User tidak berhak mengakses");
            }
        }

        $lastCol = "D";
        $spreadsheet = new Spreadsheet();
        $sheet = $spreadsheet->getActiveSheet();
        $sheet->setCellValue("A1", 'PANDUAN CEK');
        $sheet->setCellValue("A2", $formulir->instansi->nama);

        $sheet->getStyle("A1:".$lastCol."2")
            ->getAlignment()
            ->setHorizontal(Alignment::HORIZONTAL_CENTER);
        $sheet->mergeCells('A1:'.$lastCol.'1');
        $sheet->mergeCells('A2:'.$lastCol.'2');

        $sheet->setCellValue("A4", 'No');
        $sheet->setCellValue("B4", 'Indikator');
        $sheet->setCellValue("C4", 'Bobot');
        $sheet->setCellValue("D4", 'Skor');
        $sheet->getStyle("A4:D4")->getFont()->setBold(true);

        $form = $formulir->getForm()['form'];
        $row = 4;
        $totalSkor = 0;
        foreach ($form as $iKsp => $ksp) {
            // Konsep Sistem Pencegahan
            $row++;
            $kspLabel = $ksp['label'] . ' (' . $ksp['code'] . ')';
            $sheet->setCellValue("A$row", CommonTools::numberToRomanRepresentation(($iKsp + 1)));
            $sheet->setCellValue("B$row", $kspLabel);
            $sheet->getStyle("A$row:B$row")->getFont()->setBold(true);
            $totalSkor += floatval($ksp['score']);

            // Indikator
            foreach ($ksp['indikators'] as $indikator) {
                $row++;
                $indikatorCode = $indikator['code'];
                $indikatorLabel = $this->normaliseHtml($indikator['label']);
                $sheet->setCellValue("A$row", $indikatorCode);
                $sheet->setCellValue("B$row", $indikatorLabel);

                // Komponen Indikator
                foreach ($indikator['komponens'] as $komponen) {
                    $row++;
                    $prefix = str_replace($indikator['code'] . '.', '', $komponen['code']) . '. ';
                    $komponenLabel = $this->normaliseHtml($komponen['label'], $prefix);
                    $sheet->setCellValue("B$row", $komponenLabel);

                    // bobot dan score level komponen indikator
                    if (!$komponen['is_header']) {
                        $sheet->setCellValue("C$row", floatval($komponen['bobot']));
                        $sheet->setCellValue("D$row", floatval($komponen['answer']['score']));
                    }
                    // Sub Komponen Indikator
                    else {
                        foreach ($komponen['subkomponens'] as $subkomponen) {
                            $row++;
                            $prefix = str_replace($komponen['code'] . '.', '', $subkomponen['code']) . '. ';
                            $subkomponenLabel = $this->normaliseHtml($subkomponen['label'], $prefix);
                            $sheet->setCellValue("B$row", $subkomponenLabel);

                            // bobot dan score level sub komponen indikator
                            $sheet->setCellValue("C$row", floatval($subkomponen['bobot']));
                            $sheet->setCellValue("D$row", floatval($subkomponen['answer']['score']));
                        }
                    }
                }
            }
        }
        // Total Skor
        $row++;
        $sheet->setCellValue("A$row", "Total Skor");
        $sheet->getStyle("A$row:B$row")
            ->getAlignment()
            ->setHorizontal(Alignment::HORIZONTAL_RIGHT);
        $sheet->mergeCells("A$row:B$row");
        $sheet->setCellValue("C$row", doubleval(number_format($totalSkor/count($form), 2)));
        $sheet->getStyle("C$row:D$row")
            ->getAlignment()
            ->setHorizontal(Alignment::HORIZONTAL_CENTER);
        $sheet->mergeCells("C$row:D$row");
        $sheet->getStyle("A$row:D$row")->getFont()->setBold(true);
        $sheet->getRowDimension($row)->setRowHeight(35);

        $styleArray = [
            'borders' => [
                'allBorders' => [
                    'borderStyle' => Border::BORDER_THIN,
                    'color' => ['argb' => 'FF000000'],
                ],
            ],
        ];
        $sheet->getStyle('A4:'. $lastCol . ($row))->applyFromArray($styleArray);

        for($i = 5; $i<=$row-1; $i++) {
            $sheet->getRowDimension($i)->setRowHeight(-1);
        }
        $sheet->getStyle('B1:B'.$row)
            ->getAlignment()->setWrapText(true);

        $sheet->getStyle('A1:A'.$row)
            ->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
        $sheet->getStyle('A1:A'.$row)
            ->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);
        $sheet->getStyle('C1:D'.$row)
            ->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
        $sheet->getStyle('C1:D'.$row)
            ->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);

        $sheet->getColumnDimension('A')->setAutoSize(true);
        $sheet->getColumnDimension('B')->setWidth(130);
        $sheet->getColumnDimension('C')->setAutoSize(true);
        $sheet->getColumnDimension('D')->setAutoSize(true);

        $writer = new Xlsx($spreadsheet);
        $filename = "Panduan Cek - " . $formulir->instansi->nama . ".xlsx";
        header('Content-type: application/vnd.ms-excel');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        return $writer->save('php://output');
    }

    public function downloadPdf($id) {
        $this->checkOneOfScopes(['pancek.admin', 'pancek.answer']);

        $params = $this->request->get();
        $params['language'] = array_key_exists('language', $params) ? $params['language'] : 'ID';
        $allowedLanguage = ['ID', 'EN'];
        $validatedRequest = $this->validate(
            $params,
            [
                'language' => ['validators' => [
                    [
                        'name' => 'inclusion_in',
                        'params' => $allowedLanguage
                    ]
                ]]
            ]
        );


        /** @var PancekFormulir $formulir */
        $formulir = PancekFormulir::findFirst($id);
        if (!$formulir) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Fomulir tidak ditemukan.");
        }
        $instansi = $formulir->instansi;
        $user = $instansi->user;
        $detailedFormulir =  $formulir->getForm();

        if ($this->hasScopes(['pancek.answer'])) {
            if ($user->um_id != $this->shared->user->um_id) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Anda tidak berhak mengakses.");
            }

            if (!$formulir->isDownloadAvailable()) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Status pengisian formulir harus lebih dari ". PancekFormulir::MINIMUM_PERCENTAGE_TO_DOWNLOAD . "%. Mohon lengkapi isian formulir.");
            }
        }

        $answered = $formulir->countAnswered();
        $indikators = $formulir->countAnswer();
        $revision = $formulir->jumlah_revisi;

        //NOTE: Cetak Hasil selalu dalam bahasa Indonesia, tidak perlu kirim 'language'
        $html = $this->formulirHtml($instansi, $answered, $indikators, $revision, $formulir->periode, $formulir->created_at, $detailedFormulir);
        $domOptions = new \Dompdf\Options();
        $domOptions->set('isRemoteEnabled', true);
        $dompdf = new \Dompdf\Dompdf($domOptions);
        $dompdf->loadHtml($html);
        $dompdf->render();
        return $dompdf->stream();
    }

    public function downloadVerifiedPdf($id) {
        $this->checkOneOfScopes(['pancek.admin', 'pancek.answer']);
        $params = $this->request->get();
        $params['language'] = array_key_exists('language', $params) ? $params['language'] : 'ID';
        $allowedLanguage = ['ID', 'EN'];
        $validatedRequest = $this->validate(
            $params,
            [
                'language' => ['validators' => [
                    [
                        'name' => 'inclusion_in',
                        'params' => $allowedLanguage
                    ]
                ]]
            ]
        );

        /** @var PancekFormulir $formulir */
        $formulir = PancekFormulir::findFirst($id);
        if (!$formulir) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Fomulir tidak ditemukan.");
        }
        $instansi = $formulir->instansi;
        $user = $instansi->user;
        $detailedFormulir =  $formulir->getForm();

        if ($this->hasScopes(['pancek.answer'])) {
            if ($user->um_id != $this->shared->user->um_id) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Anda tidak berhak mengakses.");
            }

            if (!$formulir->isDownloadVerifiedAvailable()) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Status verifikasi formulir harus lebih dari ". PancekFormulir::MINIMUM_PERCENTAGE_TO_DOWNLOAD . "%. Mohon lengkapi isian formulir.");
            }
        }

        $answered = $formulir->countAnswered();
        $indikators = $formulir->countAnswer();
        $revision = $formulir->jumlah_revisi;

        //NOTE: Cetak Hasil selalu dalam bahasa Indonesia, tidak perlu kirim 'language'
        $html = $this->formulirHtml($instansi, $answered, $indikators, $revision, $formulir->periode, $formulir->created_at, $detailedFormulir, true);
        $domOptions = new \Dompdf\Options();
        $domOptions->set('isRemoteEnabled', true);
        $dompdf = new \Dompdf\Dompdf($domOptions);
        $dompdf->loadHtml($html);
        $dompdf->render();
        return $dompdf->stream();
    }

    private function processAnswer(PancekAnswer $answer, $attachments, $keterangan = null)
    {
        $attachment_ids = [];
        if (is_array($attachments) && !empty($attachments)) {
            foreach ($attachments as $attachment) {
                $this->validate(
                    $attachment,
                    [
                        "attachment_id" => ['validators' => ['required', 'digit']]
                    ]
                );
                $attachmentRecord = Attachments::findFirst($attachment['attachment_id']);
                if ($attachmentRecord && empty($attachmentRecord->deleted_at)) {
                    $attachmentRecord->status = Attachments::STATUS_REFERRED;
                    $attachmentRecord->referer = Attachments::REFERER_PANCEK_ANSWER;
                    if (!$attachmentRecord->save()) {
                        $errors = $attachmentRecord->getMessages();
                        $error_messages = [];

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

                        throw new ValidationException($error_messages);
                    }

                    /** @var $answerAttachment PancekAnswerAttachment */
                    $answerAttachment = PancekAnswerAttachment::firstOrCreate("attachment_id = " . $attachmentRecord->id);
                    $answerAttachment->answer_id = $answer->id;
                    $answerAttachment->attachment_id = $attachmentRecord->id;
                    if ($answerAttachment->isNewRecord()) {
                        $answerAttachment->created_at = DateHelper::getNowTimezoned();
                        if($answer->verify_status == PancekAnswer::VERIFY_STATUS_DIKEMBALIKAN) {
                            $answer->verify_status = PancekAnswer::VERIFY_STATUS_REVISED;
                        }
                        else {
                            $answer->verify_status = PancekAnswer::VERIFY_STATUS_UPDATED;
                        }
                        $answer->updated_at = DateHelper::getNowTimezoned();
                    } else {
                        $answerAttachment->updated_at = DateHelper::getNowTimezoned();
                    }
                    if (!$answerAttachment->save()) {
                        $errors = $answerAttachment->getMessages();
                        $error_messages = [];

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

                        throw new ValidationException($error_messages);
                    }
                    array_push($attachment_ids, $answerAttachment->attachment_id);
                } else {
                    throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Attachment [#" . $attachment['attachment_id'] . "] tidak ditemukan");
                }
            }
        }
        elseif (is_array($attachments) && empty($attachments)) {
            if (!empty($answer->verify_status)) {
                $answer->verify_status = null;
            }
        }

        // todo apakah keterangan bisa dihapus? saat ini keterangan hanya bisa diisi/ubah oleh verifikator, tidak bisa dihapus
        if (!empty($keterangan)) {
            $answer->keterangan = $keterangan;
        }
        if (!$answer->save()) {
            $errors = $answer->getMessages();
            $error_messages = [];

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

            throw new ValidationException($error_messages);
        } else {
            $answer->syncAttachments($attachment_ids);
        }
    }

    private function normaliseHtml($text, $prefix = null) {
        $exploded = explode("<li>", $text);
        $text = "";
        if ($prefix) {
            $text .= $prefix;
        }
        foreach ($exploded as $value){
            $text .= strip_tags($value);
            $text .= "\n";
        }
        return $text;
    }

    private function formulirHtml(PancekInstansi $instansi, $answered, $indikator, $revision, $periode, $createDate, $detailedFormulir, $isVerified = false, $language = 'ID'): string
    {
        $instansiDetail = $instansi->getDetail();
        $pancekUser = $instansi->user;
        $nomor = $instansi->nomor ?? '-';
        $sektorUsaha = $instansi->sektor_usaha_nama ?? ($instansiDetail['sektor_usaha']['nama'] ?? '-');
        $htmlCeklis = "";
        $labelKey = $language == 'ID' ? 'label' : 'label_en';
        $watermarkPath = $this->config['application']['publicDir'] . '/watermark/pancek_watermark_approved.png';
        $watermarkBase64 = base64_encode(file_get_contents($watermarkPath));
        $imgSrc = "data:image/png;base64," . $watermarkBase64;
        $htmlCheck = '<td style="font-weight: bold; font-size: 20px;">V</td>';
        $htmlUncheck = '<td style="font-weight: bold; font-size: 20px;">X</td>';
        $htmlEmpty = '<td></td>';

        foreach ($detailedFormulir['form'] as $idx => $form) {
            $htmlCeklis .= '
            <tr>
                <td style="font-weight: bold;">'.CommonTools::numberToRomanRepresentation($idx+1).'</td>
                <td style="font-weight: bold;" colspan="2">'.$form[$labelKey].'</td>
            </tr>
            ';

            //<td rowspan="'.(count($indikatorItem['komponens']) + 1).'">'.$indikatorItem['code'].'</td>

            foreach($form['indikators'] as $indikatorItem) {
                $htmlCeklis .= '
                <tr>
                    <td>'.$indikatorItem['code'].'</td>
                    <td style="font-weight: bold;" colspan="2">'.$indikatorItem[$labelKey].'</td>
                </tr>
                ';

                foreach ($indikatorItem['komponens'] as $komponen) {
                    $hasAnswer = !empty($komponen['answer']) && !empty($komponen['answer']['attachments']);
                    $hasAccepted = !empty($komponen['answer']) && $komponen['answer']['verify_status'] == PancekAnswer::VERIFY_STATUS_DISETUJUI;
                    $hasRejected = !empty($komponen['answer']) && $komponen['answer']['verify_status'] == PancekAnswer::VERIFY_STATUS_DIKEMBALIKAN;
                    if ($isVerified && $hasAccepted) {
                        $checkIcon = $htmlCheck;
                    } elseif ($isVerified && $hasRejected) {
                        $checkIcon = $htmlUncheck;
                    } elseif (!$isVerified && $hasAnswer) {
                        $checkIcon = $htmlCheck;
                    } else {
                        $checkIcon = $htmlEmpty;
                    }
                    $htmlCeklis .= '
                    <tr>
                        <td>'.$komponen['code'].'</td>
                        <td '.($komponen['is_header'] ? 'colspan="2"' : '').'>'.$komponen[$labelKey].'</td>
                        '.($komponen['is_header'] ? '' : $checkIcon).'
                    </tr>
                    ';

                    foreach ($komponen['subkomponens'] as $subkomponen) {
                        $hasAnswer = !empty($subkomponen['answer']) && !empty($subkomponen['answer']['attachments']);
                        $hasAccepted = !empty($subkomponen['answer']) && $subkomponen['answer']['verify_status'] == PancekAnswer::VERIFY_STATUS_DISETUJUI;
                        $hasRejected = !empty($subkomponen['answer']) && $subkomponen['answer']['verify_status'] == PancekAnswer::VERIFY_STATUS_DIKEMBALIKAN;
                        if ($isVerified && $hasAccepted) {
                            $checkIcon = $htmlCheck;
                        } elseif ($isVerified && $hasRejected) {
                            $checkIcon = $htmlUncheck;
                        } elseif (!$isVerified && $hasAnswer) {
                            $checkIcon = $htmlCheck;
                        } else {
                            $checkIcon = $htmlEmpty;
                        }
                        $htmlCeklis .= '
                        <tr>
                            <td>'.$subkomponen['code'].'</td>
                            <td>'.$subkomponen[$labelKey].'</td>
                            '.$checkIcon.'
                        </tr>
                    ';
                    }
                }
            }
        }

        #region Format baru
        $styleVerified = $isVerified ? '
            .watermark {
                position: fixed;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                opacity: 1;
                z-index: -1; 
                pointer-events: none;
            }
            .watermark img {
                max-width: 1100px;
                max-height: 1137px;
                width: auto;
                height: auto;
            }
            .verified {
                display: inline;
                color: green;
            }
        ' : '';
        $verifiedText = $isVerified ? '(<div class="verified">Verified</div>)' : '';
        $watermarkDiv = $isVerified ? '
        <div class="watermark">
            <img src="' . $imgSrc . '" alt="Verified">
        </div>
        ' : '';

        $textVerifiedDate = '';
        if($isVerified) {
            $verifiedDate = $detailedFormulir['verified_at'] ? $detailedFormulir['verified_at'] : $detailedFormulir['reviewed_at'];
            $textVerifiedDate = '
            <tr>
                <td style="font-weight: bold;">'. ($language == 'ID' ? 'Tanggal Diverifikasi' : 'Verified Date') .'</td>
                <td>'.DateHelper::formatFromTimestamp($verifiedDate, $language).'</td>
            </tr>
            ';
        }
        $currentDateCarbon = DateHelper::getCurrentDate();
        $currentHourMinString = $currentDateCarbon->format('H:i');
        $currentDateString = DateHelper::formatFromTimestamp($currentDateCarbon->format('Y-m-d H:i:s'), $language) . ' ' . $currentHourMinString;

        $url = $this->qrCodeUrl($detailedFormulir, $isVerified);
        $qrCode = (new QRCode)->render($url);

        $result =  '
            <!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <title>PanCEK</title>
                <style>
                    body {
                        font-family: arial, sans-serif;
                        font-size: 12px;
                    }
                    '.$styleVerified.'
                    .content {
                        position: relative;
                        z-index: 1;
                    }
                    table {
                        border-collapse: collapse;
                        width: 100%;
                    }

                    td, th {
                        border: 1px solid #dddddd;
                        padding: 8px;
                    }

                    tr:nth-child(even) {
                    }
                </style>
            </head>
            <body>
                '.$watermarkDiv.'
                <div class="content"></div>
                    <img src="' . $qrCode . '" alt="QRCode" style="float: right;width: 150px;height: 150px;">
                    <h1 style="clear: both">'. ($language == 'ID' ? ('Status Pengisian Formulir PanCEK '. $verifiedText ) : ('Status Pengisian Formulir PanCEK '. $verifiedText . ' (EN TBD)')) .'</h1>
                    <div>
                        <table>
                            <tr>
                                <td style="font-weight: bold;">'. ($language == 'ID' ? 'Nama Badan Usaha' : 'Business Entity Name') .'</td>
                                <td>'.$instansi->nama.'</td>
                            </tr>
                            <tr>
                                <td style="font-weight: bold;">'. ($language == 'ID' ? 'NPWP' : 'Tax Identification Number (TIN)') .'</td>
                                <td>'.$instansi->npwp.'</td>
                            </tr>
                            <tr>
                                <td style="font-weight: bold;">'. ($language == 'ID' ? 'Nomor Induk Berusaha (NIB)' : ' Nomor Induk Berusaha (NIB) (EN TBD)') .'</td>
                                <td>'.$instansi->nib.'</td>
                            </tr>
                            <tr>
                                <td style="font-weight: bold;">'. ($language == 'ID' ? 'Nomor Surat Keputusan' : 'Decree Number') .'</td>
                                <td>'.$nomor.'</td>
                            </tr>
                            <tr>
                                <td style="font-weight: bold;">'. ($language == 'ID' ? 'Jenis Instansi' : 'Agency Type') .'</td>
                                <td>'.$instansiDetail['jenis_instansi']['nama'].'</td>
                            </tr>
                            <tr>
                                <td style="font-weight: bold;">'. ($language == 'ID' ? 'Sektor Usaha' : 'Business Sector') .'</td>
                                <td>'.$sektorUsaha.'</td>
                            </tr>
                            <tr>
                                <td style="font-weight: bold;">'. ($language == 'ID' ? 'Alamat' : 'Address') .'</td>
                                <td>'.$instansi->alamat.'</td>
                            </tr>
                            <tr>
                                <td style="font-weight: bold;">'. ($language == 'ID' ? 'Kota/Kabupaten' : 'City/Region') .'</td>
                                <td>'.$instansiDetail['kab_kota']['kota_kabupaten_name'].'</td>
                            </tr>
                            <tr>
                                <td style="font-weight: bold;">'. ($language == 'ID' ? 'Provinsi' : 'Province') .'</td>
                                <td>'.$instansiDetail['provinsi']['provinsi_name'].'</td>
                            </tr>
                            <tr>
                                <td style="font-weight: bold;">'. ($language == 'ID' ? 'No. Telepon' : 'Phone Number') .'</td>
                                <td>'.$instansi->telepon.'</td>
                            </tr>
                            <tr>
                                <td style="font-weight: bold;">'. ($language == 'ID' ? 'Periode' : 'Period') .'</td>
                                <td>'.$periode.'</td>
                            </tr>
                            <tr>
                                <td style="font-weight: bold;">'. ($language == 'ID' ? 'Tanggal Dibuat' : 'Created Date') .'</td>
                                <td>'.DateHelper::formatFromTimestamp($createDate, $language).'</td>
                            </tr>
                            '.$textVerifiedDate.'
                            <tr>
                                <td style="font-weight: bold;">'. ($language == 'ID' ? 'Tanggal Diunduh' : 'Downloaded Date') .'</td>
                                <td>'.$currentDateString.'</td>
                            </tr>
                        </table>
                    </div>
                    <br>
                    <div>
                        <table style="margin-left: auto;margin-right: auto; text-align: center">
                            <tr>
                                <th>'. ($language == 'ID' ? 'Terjawab' : 'Answered') .'</th>
                                <th>'. ($language == 'ID' ? 'Indikator' : 'Indicator') .'</th>
                                <th>'. ($language == 'ID' ? 'Revisi' : 'Revision') .'</th>
                            </tr>
                            <tr>
                                <td style="font-weight: bold; font-size: 20px;">'.$answered.'</td>
                                <td style="font-weight: bold; font-size: 20px;">'.$indikator.'</td>
                                <td style="font-weight: bold; font-size: 20px;">'.$revision.'</td>
                            </tr>
                        </table>
                    </div>
                    <br>
                    <br>
                    <h1>'. ($language == 'ID' ? ('Ceklis Pengisian PanCEK '. $verifiedText) : ('Ceklis Pengisian PanCEK '. $verifiedText . ' (EN TBD)')) .'</h1>
                    <div>
                        <table style="margin-left: auto;margin-right: auto; text-align: center">
                        <tbody style="page-break-inside: avoid;">
                            <tr>
                                <th style="font-weight: bold;">No.</th>
                                <th style="font-weight: bold;" colspan="2">'. ($language == 'ID' ? 'Indikator' : 'Indicator') .'</th>
                            </tr>
                            '.$htmlCeklis.'
                        </table>
                        </tbody>
                    </div>
                    <br>
                    <br>
                    <h1>'. ($language == 'ID' ? 'Identitas Pengisi' : 'Identitas Pengisi (EN TBD)') .'</h1>
                    <div>
                        <table>
                            <tr>
                                <td style="font-weight: bold;">'. ($language == 'ID' ? 'Nama Lengkap' : 'Full Name') .'</td>
                                <td>'.$pancekUser->umUser->nama.'</td>
                            </tr>
                            <tr>
                                <td style="font-weight: bold;">'. ($language == 'ID' ? 'No KTP' : 'Identity Number') .'</td>
                                <td>'.$pancekUser->umUser->no_ktp_sim.'</td>
                            </tr>
                            <tr>
                                <td style="font-weight: bold;">'. ($language == 'ID' ? 'Jabatan' : 'Position') .'</td>
                                <td>'.$pancekUser->jabatan.'</td>
                            </tr>
                            <tr>
                                <td style="font-weight: bold;">'. ($language == 'ID' ? 'E-mail' : 'Email') .'</td>
                                <td>'.$pancekUser->umUser->email.'</td>
                            </tr>
                            <tr>
                                <td style="font-weight: bold;">'. ($language == 'ID' ? 'Telepon' : 'Phone Number') .'</td>
                                <td>'.$pancekUser->umUser->phone.'</td>
                            </tr>
                        </table>
                    </div>
                    <br>
                    <div>
                        <i>'. ($language == 'ID' ? 'PanCEK ini diisi secara self-assesment dalam rangka membangun manajemen antipenyuapan di lingkungan korporasi dan tidak dapat dijadikan dasar bahwa korporasi tidak terkait tindak pidana' : ' PanCEK ini diisi secara self-assesment dalam rangka membangun manajemen antipenyuapan di lingkungan korporasi dan tidak dapat dijadikan dasar bahwa korporasi tidak terkait tindak pidana (EN TBD)') .'</i>
                    </div>
                </div>
            </body>
            </html>
        ';
        //<footer style="position: absolute; right: 0; bottom: 0"><i>formulir diunduh pada '.DateHelper::getCurrentDate()->format('d-M-Y h:i').'</i></footer>

        #endregion

        return $result;
    }

    public function extendSlaKppbc($id) {
        $this->checkOneOfScopes(['pancek.admin', 'pancek.monitoring']);
        if($this->hasScopes(['pancek.monitoring'])) {
            PancekUserAccess::checkEligibleForRoleObserver($this);
            $idInstansiBeaCukai = Instansi::getIdInstansiDitjenBeaCukai();
            if($this->shared->user->id_instansi != $idInstansiBeaCukai) {
                throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, "Hanya bisa diakses oleh Observer Pancek Bea Cukai");
            }
        }

        try {
            $this->db->begin();
            $pancekFormulir = PancekFormulir::findFirst($id);
            if (!$pancekFormulir) {
                throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Data Formulir tidak ditemukan");
            }
            if ($pancekFormulir->status == PancekFormulir::STATUS_VERIFIED || $pancekFormulir->status == PancekFormulir::STATUS_REVIEWED) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Formulir sudah selesai diverifikasi/direview");
            }
            if ($pancekFormulir->status == PancekFormulir::STATUS_NEW || $pancekFormulir->status == PancekFormulir::STATUS_DRAFT || $pancekFormulir->status == PancekFormulir::STATUS_NEED_TO_REVIEW) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Verifikasi belum dimulai");
            }

            $nowDateTime = DateHelper::getNowDateTimeTimeZoned();
            $endDateDateTime = new DateTime($pancekFormulir->kppbc_verify_end_date);
            if ($endDateDateTime > $nowDateTime) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Masa Verifikasi belum melewati SLA");
            }

            $slaDays = getenv('PANCEK_EXTEND_SLA_VERIF_KPPBC') !== false ? getenv('PANCEK_EXTEND_SLA_VERIF_KPPBC') : 7;
            $nowPlusSlaDateTime = clone $nowDateTime;
            $nowPlusSlaDateTime->modify('+' . $slaDays . ' days');
            $nowPlusSlaFormatted = DateHelper::formatDateTime($nowPlusSlaDateTime);

            $pancekFormulir->kppbc_verify_end_date = $nowPlusSlaFormatted;
            $pancekFormulir->save();

            $logVerifikasi = new PancekFormulirLogVerifikasi();
            $logVerifikasi->um_id = $this->userID;
            $logVerifikasi->formulir_id = $id;
            $logVerifikasi->verify_status = PancekFormulirLogVerifikasi::STATUS_EXTENDED;
            $logVerifikasi->sla_started_at = $pancekFormulir->kppbc_verify_start_date;
            $logVerifikasi->sla_ended_at = DateHelper::formatDateTime($endDateDateTime);
            $logVerifikasi->save();

            $this->db->commit();
            return new SimpleResponse($pancekFormulir);
        }
        catch (Exception $exception) {
            $this->db->rollback();
            throw $exception;
        }
    }

    public function assignVerifikator($id)
    {
        $this->checkOneOfScopes(['admin.view', 'pancek.admin', 'pancek.kppbc.assignor']);
        $rawParams = $this->request->getJsonRawBody(true);
        $params = $this->validate(
            $rawParams,
            [
                'um_id' => ['validators' => ['digit', 'required']],
            ]
        );

        $idInstansiBeaCukai = Instansi::getIdInstansiDitjenBeaCukai();
        $idInstansiUser = $this->shared->user->id_instansi;
        if($this->hasScopes(['pancek.kppbc.assignor']) && $idInstansiBeaCukai != $idInstansiUser) {
            throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, 'User is not allowed to perform this action');
        }

        $formulir = PancekFormulir::findFirst($id);
        if(!$formulir) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Formulir tidak ditemukan");
        }
        $badanUsaha = $formulir->instansi;
        $kppbcId = $badanUsaha->kppbc_id;
        if($kppbcId == null) {
            throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, "Badan Usaha belum di-assign ke KPPBC");
        }

        $kppbcUserAssignor = PancekKppbcUser::findFirst([
            'conditions' => 'kppbc_id = :kppbc_id: 
                            AND um_id = :um_id: 
                            AND user_type = :user_type:
                            AND deleted_at IS NULL',
            'bind' => [
                'kppbc_id' => $kppbcId,
                'um_id' => $this->userID,
                'user_type' => PancekKppbcUser::USER_TYPE_ASSIGNOR
            ]
        ]);

        if(!$kppbcUserAssignor) {
            throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, "Anda belum ditugaskan pada KPPBC terpilih");
        }

        $kppbcUserVerifikator = PancekKppbcUser::findFirst([
            'conditions' => 'kppbc_id = :kppbc_id: 
                            AND um_id = :um_id: 
                            AND user_type = :user_type:
                            AND deleted_at IS NULL',
            'bind' => [
                'kppbc_id' => $kppbcId,
                'um_id' => $params['um_id'],
                'user_type' => PancekKppbcUser::USER_TYPE_VERIFIATOR
            ]
        ]);

        if(!$kppbcUserVerifikator) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "User yang diajukan tidak terdaftar sebagai verifikator di KPPBC terpilih");
        }

        $formulir->kppbc_um_id = $params['um_id'];
        $formulir->save();

        $pancekService = new PancekService($this);
        $pancekService->handleAssignmentNotification($params['um_id']);

        return new SimpleResponse($formulir);
    }

    private function qrCodeUrl($formulir, $isVerified)
    {
        /*switch ($isVerified) {
            case PancekFormulir::STATUS_VERIFIED:
                $verificator = UmUser::findFirst($formulir["verified_by"]);
                if(!$verificator) {
                    throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Verifikator tidak ditemukan");
                }
                if ($verificator->id_instansi == Instansi::getIdInstansiDitjenBeaCukai()) {
                    $endUrl = "kementerian_keuangan_beacukai";
                } elseif ($verificator->id_instansi == Instansi::getIdInstansiDitjenPupr()) {
                    $endUrl = "kementerian_pekerjaan_umum";
                } else {
                    $endUrl = "kpk";
                }
                $url = getenv("WEB_BASE_URL") . "/jendela-pencegahan/$endUrl";
                break;
            default:
                $url = getenv("WEB_BASE_URL") . "/jendela-pencegahan/non_verifikasi";
        }

        return $url;*/

        $data = [
            "nama_badan_usaha" => $formulir["instansi"]->nama,
            "npwp" => $formulir["instansi"]->npwp,
            "provinsi" => $formulir["instansi"]->provinsi->provinsi_name,
            "kabkota" => $formulir["instansi"]->kabkota->kota_kabupaten_name,
            "sektor_usaha" => $formulir["instansi"]->sektorusaha->nama,
        ];

        $code = base64_encode(json_encode($data));

        return getenv("WEB_BASE_URL") . "/qrcode?ref=pancek&code=" . $code;
    }
}
