<?php

class PancekUserController extends BaseController
{
    #region public
    /**
     * @return ObjectResponse
     * @throws CustomErrorException
     * proses registrasi user menginput data akun aplikasi dan instansi
     * user akan mendapatkan role 'pengguna' dan bisa melakukan login ke aplikasi
     * instansi diset status VERIFIED
     */
    public function register()
    {
        $user = new UmUser();

        $rawBody = $this->request->getPost();
        $params = $this->validateStoreRequest($rawBody, $user);

        $isVerified = boolval($params['is_verified']);

        return $this->saveUser($user, $params, $isVerified);
    }

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

        $request = $this->request->getJsonRawBody(true);
        $rules = [
            'email' => ['validators' => ['required', 'email']],
            'nama' => ['validators' => ['required']],
            'phone' => ['validators' => ['required', 'digit']],
            'jenis_kelamin' => ['validators' =>  [[
                'name' => 'inclusion_in',
                'params' => ['L', 'P', 'T']
            ]]],
            'jabatan' => ['validators' => ['required', [
                'name' => 'max_string',
                'params' => 255
            ], [
                'name' => 'min_string',
                'params' => 3
            ]]],
            'notify' => ['validators' => ['required', [
                'name' => 'inclusion_in',
                'params' => [0,1]
            ]]]
        ];
        $isChangePwd = false;
        if (array_key_exists('password', $request) ) {
            $rules['password'] = ['validators' => [
                'confirm_password',
                [
                    'name' => 'min_string',
                    'params' => 8
                ]
            ]];
            $isChangePwd = true;
        }
        $validatedRequest = $this->validate($request, $rules);

        /** @var $pancekUser PancekUser */
        $pancekUser = PancekUser::findByUmId($umId, false);

        if (!$pancekUser->umUser) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Akun User tidak ditemukan");
        }

        $this->db->begin();
        try{
            $oldData = LogModeration::extractUmUser($umId);

            // save PancekUser
            $pancekUser->jabatan = $validatedRequest['jabatan'];
            if (!$pancekUser->save()) {
                $this->throwObjectValidationException($pancekUser);
            }

            // save UmUser
            $umUser = $pancekUser->umUser;
            UserHelper::validateEmailAvailability(strtolower($validatedRequest['email']), strtolower($umUser->email));
            UserHelper::validatePhoneAvailability($validatedRequest['phone'], $umUser->phone);
            if (!\UserHelper::validateBlacklistedEmail($validatedRequest['email'])) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Email yang Anda masukkan tidak valid");
            }
            if ($validatedRequest['email'] !== $oldData['email']) {
                UserHelper::reqVerifyEmail(
                    $umUser,
                    $validatedRequest['email'],
                    new MailHelper($this->config["mail_config"]),
                    $this->config["web_base_url"]
                );
                $umUser->is_email_verified = false;
            }
            $umUser->email = $validatedRequest['email'];
            $umUser->phone = $validatedRequest['phone'];
            $umUser->nama = $validatedRequest['nama'];
            $umUser->jenis_kelamin = $validatedRequest['jenis_kelamin'];
            if (array_key_exists('password', $validatedRequest)) {
                $umUser->um_user_new_password = $this->security->hash($validatedRequest['password']);
            }
            if (!$umUser->save()) {
                $this->throwObjectValidationException($umUser);
            }

            // notify user by email
            if ($validatedRequest['notify']) {
                $this->notifyUserUpdated($umUser, $validatedRequest);
            }

            $newData = LogModeration::extractUmUser($umId);
            $action = [LogModeration::ACTION_UPDATE_UMUSER];
            if ($isChangePwd) array_push($action, LogModeration::ACTION_CHANGEPWD_UMUSER);
            LogModeration::loggedUmUser(
                $action,
                LogModeration::ACTIONMENU_ADMINISTRASI_PANCEK,
                $umId,
                $oldData,
                $newData,
                $this->shared->user->um_id
            );

            $this->db->commit();

            return new SimpleResponse(PancekUser::findByUmId($umId));
        } catch (\Exception $e) {
            $this->db->rollback();
            throw $e;
        }
    }

    /**
     * @param $um_id
     * @return SimpleResponse
     * @throws CustomErrorException setelah melakukan registrasi, instansi user tsb perlu di verifikasi terlebih dahulu
     * agar mendapatkan role 'user_pancek' kemudian user dapat mengakses dashboard dan mengisi formulir PanCek
     * instansi diset status VERIFIED
     */
    public function verify($um_id)
    {
        $this->checkOneOfScopes(['pancek.admin', 'pancek.verify']);

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

            /** @var $userPancek PancekUser */
            $userPancek = PancekUser::findFirst(['conditions' => 'um_id = :id:', 'bind' => ['id' => $um_id]]);
            if (!$userPancek) throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "User tidak ditemukan");

            /** @var $instansi PancekInstansi */
            $instansi = PancekInstansi::findFirst(['conditions' => 'id = :id:', 'bind' => ['id' => $userPancek->instansi_id]]);
            if (!$instansi) throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Instansi tidak ditemukan");
            if ($instansi->status == PancekInstansi::STATUS_VERIFIED) throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Instansi telah diverifikasi sebelumnya");
            if (!is_null($instansi->deleted_at)) throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Instansi telah dihapus");

            $instansi->status = PancekInstansi::STATUS_VERIFIED;
            $instansi->verified_at = date("Y-m-d H:i:s");
            $instansi->verified_by = $this->shared->user->um_id;
            if (!$instansi->save()) throw new CustomErrorException(BaseResponse::UNEXPECTED_ERROR, "Gagal menyimpan data instansi");

            // todo apakah instansi pancek yang telah diverifikasi perlu di input ke table instansi?
            $instansiTipe = InstansiTipe::findFirst([
                'conditions' => 'lower(nama) LIKE :nama:',
                'bind' => [
                    'nama' => strtolower($instansi->jenisInstansi->nama)
                ]
            ]);
            if (!$instansiTipe) {
                $instansiTipe = new InstansiTipe();
                $instansiTipe->nama = $instansi->jenisInstansi->nama;
                $instansiTipe->stranas_filter = false;
                $instansiTipe->created_by = $this->shared->user->um_id;
                try {
                    $instansiTipe->save();
                } catch (PDOException $e) {
                    $this->db->rollback();
                    $this->db->query("SELECT setval('jaga.instansi_tipe_id_seq', COALESCE((SELECT MAX(id)+1 FROM jaga.instansi_tipe), 1), false);")->execute();
                    $this->db->begin();

                    $instansiTipe = new InstansiTipe();
                    $instansiTipe->nama = $instansi->jenisInstansi->nama;
                    $instansiTipe->stranas_filter = false;
                    $instansiTipe->created_by = $this->shared->user->um_id;
                    if (!$instansiTipe->save()) {
                        throw new CustomErrorException(BaseResponse::UNEXPECTED_ERROR, "Gagal menyimpan Tipe Instansi");
                    }
                }
            }
            $jagaInstansi = new Instansi();
            $jagaInstansi->nama = $instansi->nama;
            $jagaInstansi->id_provinsi = $instansi->provinsi_id;
            $jagaInstansi->id_kota_kabupaten = $instansi->kab_kota_id;
            $jagaInstansi->id_tipe = $instansiTipe->id;
            $jagaInstansi->flag_dana_desa = 0;
            $jagaInstansi->flag_pak = false;
            $jagaInstansi->show_to_public = 0;
            $jagaInstansi->is_stranas = 0;
            try {
                $jagaInstansi->save();
            } catch (PDOException $e) {
                $this->db->rollback();
                $this->db->query("SELECT setval('jaga.instansi_id_seq', COALESCE((SELECT MAX(id)+1 FROM jaga.instansi), 1), false);")->execute();

                $this->db->begin();
                $instansiTipe = InstansiTipe::findFirst([
                    'conditions' => 'lower(nama) LIKE :nama:',
                    'bind' => [
                        'nama' => strtolower($instansi->jenisInstansi->nama)
                    ]
                ]);
                if (!$instansiTipe) {
                    $instansiTipe = new InstansiTipe();
                    $instansiTipe->nama = $instansi->jenisInstansi->nama;
                    $instansiTipe->stranas_filter = false;
                    $instansiTipe->created_by = $this->shared->user->um_id;
                    if (!$instansiTipe->save()) {
                        throw new CustomErrorException(BaseResponse::UNEXPECTED_ERROR, "Gagal menyimpan Tipe Instansi");
                    }
                }
                if (!$instansi->save()) {
                    throw new CustomErrorException(BaseResponse::UNEXPECTED_ERROR, "Gagal menyimpan data instansi");
                }
                $jagaInstansi->id_tipe = $instansiTipe->id;
                if (!$jagaInstansi->save()) {
                    throw new CustomErrorException(BaseResponse::UNEXPECTED_ERROR,  "Gagal menyimpan Instansi JAGA");
                }
            }

            // begin save user roles
            $user = $userPancek->umUser;
            $user->checkmark_verified = 1;
            $user->id_instansi = $jagaInstansi->id;
            if (!$user->save()) {
                throw new CustomErrorException(BaseResponse::UNEXPECTED_ERROR, "Gagal menyimpan data user");
            }
            $roles = ['pengguna', 'user_pancek'];
            $users_roles = Roles::find([
                'conditions' => 'name in ({roles:array})',
                'columns' => 'id',
                'bind' => [
                    'roles' => $roles
                ]
            ])->toArray();

            $user_roles_ids = [];
            foreach ($users_roles as $key => $role) {
                array_push($user_roles_ids, $role['id']);
            }

            $user->syncRoles($user_roles_ids);
            // end save user roles

            // notifikasi ke user instansi bahwa pendaftaran instansi telah diverifikasi
            $this->notifyUser($user, $instansi);
            // end notifikasi

            $this->db->commit();

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

    public function list()
    {
        $this->checkOneOfScopes(['pancek.admin', 'pancek.monitoring']);
        $params = $this->request->get();

        $params['username'] = array_key_exists('username', $params) ? $params['username'] : '';
        $params['email'] = array_key_exists('email', $params) ? $params['email'] : '';
        $params['nama'] = array_key_exists('nama', $params) ? $params['nama'] : '';
        $params['instansi_nama'] = array_key_exists('instansi_nama', $params) ? $params['instansi_nama'] : '';
        $params['keyword'] = array_key_exists('keyword', $params) ? $params['keyword'] : '';
        $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',
            'username',
            'nama',
            'email',
            'instansi_nama',
            'created_at',
            'updated_at'
        ];
        $allowed_order_direction = ['asc', 'desc'];
        $bind_order = [
            'id' => 'UmUser.um_id',
            'username' => 'UmUser.um_user_name',
            'nama' => 'UmUser.nama',
            'email' => 'UmUser.email',
            'instansi_nama' => 'PancekInstansi.nama',
            'created_at' => 'PancekUser.created_at',
            'updated_at' => 'PancekUser.updated_at'
        ];

        $this->validate(
            $params,
            [
                'username' => ['validators' => []],
                'email' => ['validators' => []],
                'nama' => ['validators' => []],
                'nama_instansi' => ['validators' => []],
                'keyword' => ['validators' => []],
                '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
                ]]]
            ]
        );

        $result = PancekUser::query()
            ->columns("
            UmUser.um_user_name, UmUser.nama, UmUser.email, UmUser.phone, UmUser.jenis_kelamin, 
            PancekUser.id, PancekUser.um_id, PancekUser.jabatan, PancekUser.created_at, PancekUser.updated_at, PancekUser.deleted_at, 
            PancekInstansi.id, PancekInstansi.nama as instansi_nama")
            ->leftJoin('UmUser', 'UmUser.um_id = PancekUser.um_id')
            ->leftJoin('PancekInstansi', 'PancekInstansi.id = PancekUser.instansi_id')
            ->where(
                "UmUser.um_user_name ILIKE :username: AND " .
                "UmUser.nama ILIKE :nama: AND " .
                "UmUser.email ILIKE :email: AND " .
                "PancekInstansi.nama ILIKE :instansi_nama: AND " .
                "PancekUser.deleted_at IS NULL AND " .
                "(UmUser.um_user_name ILIKE :keyword: OR UmUser.nama ILIKE :keyword: OR UmUser.email ILIKE :keyword: OR PancekInstansi.nama ILIKE :keyword:)"
            )
            ->bind([
                "username" => '%' . $params['username'] . '%',
                "nama" => '%' . $params['nama'] . '%',
                "email" => '%' . $params['email'] . '%',
                "instansi_nama" => '%' . $params['instansi_nama'] . '%',
                "keyword" => '%' . $params['keyword'] . '%',
            ])
            ->orderBy($bind_order[$params['order_by']] . " " . $params['order_direction'])
            ->limit($params['limit'], $params['offset'])
            ->execute()
            ->toArray();
        $total = $this->rawQuery("
            SELECT count(*) as total FROM jaga.pancek_user 
            LEFT JOIN jaga.um_user ON um_user.um_id = pancek_user.um_id 
            LEFT JOIN jaga.pancek_instansi ON pancek_instansi.id = pancek_user.instansi_id
            WHERE um_user.um_user_name ILIKE :username
            AND um_user.nama ILIKE :nama
            AND um_user.email ILIKE :email
            AND pancek_instansi.nama ILIKE :instansi_nama
            AND (um_user.um_user_name ILIKE :keyword OR um_user.nama ILIKE :keyword OR um_user.email ILIKE :keyword OR pancek_instansi.nama ILIKE :keyword)
            AND pancek_user.deleted_at IS NULL
        ",
        [
                "username" => '%' . $params['username'] . '%',
                "nama" => '%' . $params['nama'] . '%',
                "email" => '%' . $params['email'] . '%',
                "instansi_nama" => '%' . $params['instansi_nama'] . '%',
                "keyword" => '%' . $params['keyword'] . '%',
        ])[0]['total'];

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

    public function detail($um_id)
    {
        $this->checkIsAuthenticated();
        return new SimpleResponse($this->generalInfo($um_id));
    }

    public function info($um_id)
    {
        $this->checkIsAuthenticated();
        $result = $this->generalInfo($um_id);

        $formulir = PancekFormulir::findActiveByInstansiId($result['instansi_id']);
        $result['formulir'] = null;

        if ($formulir) {
            $result['formulir'] = $formulir->stat();
        }

        return new SimpleResponse($result);
    }

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

        $this->db->begin();
        try {
            /** @var UmUser $user */
            $user = UmUser::findFirst(['conditions' => 'um_id = :id:', 'bind' => ['id' => $umId]]);
            if (!$user) {
                throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Akun tidak ditemukan.");
            }
            /** @var PancekUser $pancekUser */
            $pancekUser = PancekUser::findFirst(['conditions' => 'um_id = :id: AND deleted_at IS NULL', 'bind' => ['id' => $umId]]);
            if (!$pancekUser) {
                throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "User PanCek tidak ditemukan.");
            }

            UserHelper::deletion($user);

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

    #region proteced
    protected function generalInfo($um_id)
    {
        if ($this->shared->user->um_id != $um_id) {
            $this->checkOneOfScopes(['pancek.admin', 'pancek.monitoring', 'pancek.verify', 'pancek.bk.verify']);
        }
        $result = PancekUser::findByUmId($um_id);
        $result['instansi']['foto_url'] = MediaHelper::getUrl($result['instansi']["foto"], $this->config, $this->minio);
        $result['um_user']['foto_url'] = MediaHelper::getPresignedUrlFoto($result['um_user'], UmUser::class, $this->config, $this->minio);
        return $result;
    }
    protected function validateStoreRequest($request, $user)
    {
        $allowed_gender = ['L','P','T'];

        $rules = [
            'is_verified' => ['validators' => [
                'required',
                'digit',
                [
                    'name' => 'inclusion_in',
                    'params' => [0,1]
                ]
            ]],
            'username' => ['validators' => [
                'required',
                'alpha_dash',
                [
                    'name' => 'min_string',
                    'params' => 6
                ]
            ]],
            'email' => ['validators' => [
                'required',
                'email',
                [
                    'name' => 'max_string',
                    'params' => 100
                ]
            ]],
            'nama' => ['validators' => ['required']],
            'foto' => ['validators' => []],
            'phone' => ['validators' => ['required', 'digit']],
            'jenis_kelamin' => ['validators' =>  [[
                'name' => 'inclusion_in',
                'params' => $allowed_gender
            ]]],
            'jabatan' => ['validators' => ['required', [
                'name' => 'max_string',
                'params' => 255
            ], [
                'name' => 'min_string',
                'params' => 3
            ]]],
            'nomor' => ['validators' => [
                [
                    'name' => 'max_string',
                    'params' => 100
                ]
            ]],
            'npwp' => ['validators' => [
                'required',
                'numeric',
                [
                    'name' => 'min_string',
                    'params' => 6
                ]
            ]],
            'no_ktp_sim' => ['validators' => [
            ]],
            'jenis_instansi_id' => ['validators' => ['required', 'digit']],
            'sektor_usaha_id' => ['validators' => ['required', 'digit']],
            'nama_instansi' => ['validators' => [
                'required',
                [
                    'name' => 'min_string',
                    'params' => 5
                ],
                [
                    'name' => 'max_string',
                    'params' => 255
                ]
            ]],
            'provinsi_id' => ['validators' => ['required', 'digit']],
            'kab_kota_id' => ['validators' => ['required', 'digit']],
            'alamat' => ['validators' => [
                [
                    'name' => 'max_string',
                    'params' => 255
                ]
            ]],
            'telepon' => ['validators' => [
                'required',
                [
                    'name' => 'max_string',
                    'params' => 15
                ]
            ]],
            'email_instansi' => ['validators' => [
                'required',
                [
                    'name' => 'max_string',
                    'params' => 100
                ],
                'email'
            ]],
            'ceklis_kepentingan' => ['validators' => [['name' => 'max_string', 'params' => 255]]],
            'nib' => ['validators' => [
//                'required',
//                'numeric',
//                [
//                    'name' => 'min_string',
//                    'params' => 13
//                ]
            ]],
            'kppbc_id' => ['validators' => []],
        ];

        // if new record, must have a password
        if($user->um_id == null) {
            $rules['password'] = ['validators' => [
                'required',
                'confirm_password',
                [
                    'name' => 'min_string',
                    'params' => 8
                ]
            ]];
            $rules['confirm_password'] = ['validators' => ['required']];

            $request['password'] = AuthHelper::decryptPassword($request);
            $request['confirm_password'] = AuthHelper::decryptPassword($request, 'confirm_password', 'h_confirm_password');
            AuthHelper::validatePassword($request['password']);
        }
        elseif (array_key_exists('password', $request) ) {
            $rules['password'] = ['validators' => [
                'confirm_password',
                [
                    'name' => 'min_string',
                    'params' => 8
                ]
            ]];

            $request['password'] = AuthHelper::decryptPassword($request);
            $request['confirm_password'] = AuthHelper::decryptPassword($request, 'confirm_password', 'h_confirm_password');
            AuthHelper::validatePassword($request['password']);
        }

        if (array_key_exists('sektor_usaha_id', $request) && $request['sektor_usaha_id'] == PancekSektorUsaha::SEKTOR_USAHA_LAINNYA) {
            $rules['sektor_usaha_nama'] = ['validators' => [
                'required',
                [
                    'name' => 'min_string',
                    'params' => 6
                ],
                [
                    'name' => 'max_string',
                    'params' => 255
                ]
            ]];
        }

        $params = $this->filterExpectedRequest($request, $rules);

        $pancekInstansiEmailExist = PancekInstansi::findFirst(['conditions' => 'lower(email) = :email: AND deleted_at IS NULL', 'bind' => ['email' => strtolower($params['email_instansi'])]]);
        if ($pancekInstansiEmailExist) throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Email instansi tersebut telah digunakan.");

        $pancekInstansiPhoneExist = PancekInstansi::findFirst(['conditions' => 'telepon = :phone: AND deleted_at IS NULL', 'bind' => ['phone' => strtolower($params['telepon'])]]);
        if ($pancekInstansiPhoneExist) throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Nomor telepon instansi tersebut telah digunakan.");

        $jenisInstansiExist = PancekJenisInstansi::findFirst(['conditions' => 'id = :id: AND deleted_at IS NULL', 'bind' => ['id' => $params['jenis_instansi_id']]]);
        if (!$jenisInstansiExist) throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Jenis Instansi tidak ditemukan atau telah dihapus");

        $sektorUsahaExist = PancekSektorUsaha::findFirst(['conditions' => 'id = :id: AND deleted_at IS NULL', 'bind' => ['id' => $params['sektor_usaha_id']]]);
        if (!$sektorUsahaExist) throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Sektor tidak ditemukan atau telah dihapus");

        $provinsiExist = MasterProvinsi::findFirst(['conditions' => 'provinsi_code = :id:', 'bind' => ['id' => $params['provinsi_id']]]);
        if (!$provinsiExist) throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Provinsi tidak ditemukan");

        $kabKotaExist = MasterKotaKabupaten::findFirst(['conditions' => 'kota_kabupaten_code = :id:', 'bind' => ['id' => $params['kab_kota_id']]]);
        if (!$kabKotaExist) throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Kabupaten/Kota tidak ditemukan");

        \UserHelper::validateUsernameAvailability($params['username'], $user->um_user_name);
        \UserHelper::validateEmailAvailability($params['email'], $user->email);
        if (!\UserHelper::validateBlacklistedEmail($params['email'])) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Email yang Anda masukkan tidak valid");
        }
        if (!\UserHelper::validateBlacklistedEmail($params['email_instansi'])) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Email Badan Usaha yang Anda masukkan tidak valid");
        }
        \UserHelper::validatePhoneAvailability($params['phone'], $user->phone);

        // jika flag terverifikasi AHU, assign data parameter dengan data dari AHU
        if (boolval($params['is_verified'])) {
            $ahuClient = new AhuClient($this->config["ahu_prop"]);
            try {
                $ahu = $ahuClient->getTransaksiBakum($params['nama_instansi']);
            } catch (\Exception $e) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Tidak dapat melakukan koneksi ke pusat data. Silakan coba beberapa saat lagi");
            }
            if (empty($ahu)) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Data instansi tidak ditemukan. Mohon periksa kembali data registrasi yang telah diisi");
            }

            $params['nomor'] = $ahu['no_surat_keputusan'];
            $params['nama_instansi'] = $ahu['nama_pt'];
            $params['npwp'] = $ahu['npwp_perseroan'];
        }

        $pancekInstansiExist = PancekInstansi::findFirst(['conditions' => 'npwp = :npwp: and deleted_at is null', 'bind' => ['npwp' => $params['npwp']]]);
        if ($pancekInstansiExist) throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Instansi [" . $params['npwp'] . "] telah terdaftar sebelumnya.");

        if($params['ceklis_kepentingan'] == '0;1;0') {
            if($params['kppbc_id'] == null) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "KPPBC harus dipilih.");
            }
            else {
                $pancekKppbcExist = PancekKppbc::findFirst($params['kppbc_id']);
                if(!$pancekKppbcExist) throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "KPPBC tidak ditemukan.");
            }
        }

        return $params;
    }

    protected function saveUser($user, $params, $isVerified = false)
    {
        try {
            $this->db->begin();
            // MoM 15062022, user yg telah registrasi langsung dapat mengisi formulir PanCek
            // hanya di flagging pada instansi
            // VERIFIED instansi sudah dicek di data AHU/pusat data lain
            // REGISTERED instansi belum dicek di data AHU/pusat data lain
            $roles = ['pengguna', 'user_pancek'];
            if ($isVerified) {
                $instansiStatus = PancekInstansi::STATUS_VERIFIED;
                $userCheckmark = 1;
            } else {
                $instansiStatus = PancekInstansi::STATUS_REGISTERED;
                $userCheckmark = 0;
            }

            // upload pictures
            $userFoto = null;
            $instansiFoto = null;
            if ($this->request->hasFiles()) {
                $uploadedFiles = $this->request->getUploadedFiles();
                $random = new \Phalcon\Security\Random();
                UserHelper::validateProfilePicture($uploadedFiles);
                foreach ($uploadedFiles as $file) {
                    // upload profile picture
                    if ($file->getKey() === "profile_picture" || $file->getKey() === "instansi_picture") {
                        if (substr($file->getRealType(), 0, 5) !== "image") {
                            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Update User Gagal', [[
                                "field" => "image",
                                "filename" => $file->getName(),
                                "message" => "Tipe file tidak diizinkan"
                            ]]);
                        }

                        $shortName = $random->uuid() . "." . pathinfo($file->getName(), PATHINFO_EXTENSION);

                        if (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::LOCAL) {
                            $filename = getenv("USER_IMAGE_FOLDER") . DIRECTORY_SEPARATOR;
                            if (!file_exists($filename) && !is_dir($filename)) {
                                mkdir($filename, 0770, true);
                            }

                            $filename .= DIRECTORY_SEPARATOR . $shortName;
                            $file->moveTo($filename);
                            if ($file->getKey() === "profile_picture") {
                                $userFoto = $shortName;
                            } elseif ($file->getKey() === "instansi_picture") {
                                $instansiFoto = $shortName;
                            }

                        } elseif (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::MINIO) {
                            /** @var MinioService $minioService */
                            $minioService = $this->minio;
                            if ($minioService->isSecondaryS3Available()) {
                                $minioService->baseUploadUsingSecondaryS3(
                                    $file->getTempName(),
                                    $file->getRealType(),
                                    false,
                                    'users_profpic' . DIRECTORY_SEPARATOR . $shortName
                                );
                            } else {
                                $minioService->base_upload_attachment(
                                    $file->getTempName(),
                                    $file->getRealType(),
                                    false,
                                    'users_profpic' . DIRECTORY_SEPARATOR . $shortName
                                );
                            }
                            if ($file->getKey() === "profile_picture") {
                                $userFoto = $shortName;
                            } elseif ($file->getKey() === "instansi_picture") {
                                $instansiFoto = $shortName;
                            }
                        }
                    }
                }
            }

            // begin save user
            $needEmailVerified = false;
            $user->assign($params);
            $user->um_user_name = $params['username'];
            $user->um_tenant_id = -1234;
            $user->um_changed_time = date("Y-m-d H:i:s");
            $user->foto = $userFoto;
            $user->checkmark_verified = $userCheckmark;
            if (empty($user->um_id)) {
                $user->is_email_verified = false;
                $needEmailVerified = true;
            }

            if(array_key_exists('password', $params)) {
                $hashed_password = base64_encode(hash("sha256", $params['password'], true));
                $user->um_user_new_password = $this->security->hash($params['password']);
                $user->um_user_password = $hashed_password;
            }

            if(!$user->save()) {
                throw new CustomErrorException(BaseResponse::UNEXPECTED_ERROR, "Akun pengguna gagal disimpan");
            }
            // end save user

            if ($needEmailVerified) {
                UserHelper::reqVerifyEmail(
                    $user,
                    $user->email,
                    new MailHelper($this->config["mail_config"]),
                    $this->config["web_base_url"]
                );
            }

            // begin save user roles
            $users_roles = Roles::find([
                'conditions' => 'name in ({roles:array})',
                'columns' => 'id',
                'bind' => [
                    'roles' => $roles
                ]
            ])->toArray();

            $user_roles_ids = [];
            foreach ($users_roles as $key => $role) {
                array_push($user_roles_ids, $role['id']);
            }

            $user->syncRoles($user_roles_ids);
            // end save user roles

            // begin save instansi pancek
            $instansiPancek = new PancekInstansi();
            $instansiPancek->assign($params);
            $instansiPancek->nama = $params['nama_instansi'];
            $instansiPancek->email = $params['email_instansi'];
            $instansiPancek->status = $instansiStatus;
            $instansiPancek->foto = $instansiFoto;
            $instansiPancek->ceklis_kepentingan = $params['ceklis_kepentingan'];
            if($params['nib'] != null) {
                $instansiPancek->nib = $params['nib'];
            }
            if($params['kppbc_id'] != null) {
                $instansiPancek->kppbc_id = $params['kppbc_id'];
            }
            if (!$instansiPancek->save()) throw new CustomErrorException(BaseResponse::UNEXPECTED_ERROR, "Data Instansi gagal disimpan");
            // end save instansi pancek

            // begin save user pancek
            $userPancek = new PancekUser();
            $userPancek->um_id = $user->um_id;
            $userPancek->jabatan = $params['jabatan'];
            $userPancek->instansi_id = $instansiPancek->id;
            if (!$userPancek->save()) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Data User gagal disimpan");
            }
            // end save user pancek

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

    protected function delete($umId) {
        $pancekUser = PancekUser::findFirst(['conditions' => 'um_id = :id:', 'bind' => ['id' => $umId]]);
        if ($pancekUser === false) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "User Pancek tidak ditemukan.");
        }

        if ($pancekUser->delete() === false) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Gagal menghapus User Pancek.");
        }
    }

    /**
     * @param UmUser $user
     * @param PancekInstansi $instansi
     */
    protected function notifyUser(UmUser $user, PancekInstansi $instansi)
    {
        $subject = "[JAGA] Instansi Telah Diverifikasi";
        $message = "Hi Sahabat JAGA, terima kasih telah menggunakan Aplikasi JAGA."
            . "\n\nPermintaan pendaftaran Instansi kamu telah diverifikasi. Berikut informasi data registrasi kamu :"
            . "\n\nNama Instansi: " . $instansi->nama
            . "\nNPWP: " . $instansi->npwp
            . "\nTelepon: " . $instansi->telepon
            . "\n\nBerikut akses yang dapat digunakan untuk login ke Aplikasi JAGA:"
            . "\n\nUsername: " . $user->um_user_name
            . "\n\natau klik link berikut " . $this->config["web_base_url"]
            . "\n\nUntuk memulai mengisi formulir dapat dilakukan melalui link berikut ." . $this->config["web_base_url"] . "/pancek/form"
            . "\n\nJika ada ketidaksesuaian pada data di atas, kamu dapat mengubah data kamu pada halaman dashboard."
            . "\nJangan khawatir, semua data kamu aman di JAGA."
            . "\nAkses JAGA dan bantu kami untuk Cegah Korupsi dari Ujung Jari."
            . "\n\nPerhatian: E-mail ini (termasuk seluruh lampirannya, bila ada) dikirim otomatis oleh aplikasi dan hanya ditujukan kepada penerima yang tercantum di atas. Jika Anda bukan penerima yang dituju, maka Anda tidak diperkenankan untuk memanfaatkan, menyebarkan, mendistribusikan, atau menggandakan e-mail ini beserta seluruh lampirannya. Mohon untuk tidak membalas email ini."
        ;

        $mail = new MailHelper($this->config["mail_config"]);
        $mail->queue($user->email, $subject, $message, false, $this->shared->user->um_id);
        $mail->queue($instansi->email, $subject, $message, false, $this->shared->user->um_id);
    }

    protected function notifyUserUpdated(UmUser $user, $request) {
        if (array_key_exists('password', $request)) {
            $subject = "[JAGA] Perubahan Data dan Password Akun JAGA";
            $message = "Hi Sahabat JAGA, terima kasih telah menggunakan Aplikasi JAGA."
                . "\n\nKamu menerima email ini karena adanya perubahan data pada akun JAGA kamu. Berikut informasi perubahan data pada Akun kamu :"
                . "\n\n-Nama: " . $user->nama
                . "\n-Telepon: " . $user->phone
                . "\n-Email: " . $user->email
                . "\n\ndan melakukan permintaan perubahan password pada akun JAGA kamu. Berikut akses yang dapat digunakan untuk login ke Aplikasi JAGA:"
                . "\n\n-Username: " . $user->um_user_name
                . "\n-Password: " . $request['password']
                . "\n\natau klik link berikut " . $this->config["web_base_url"]
                . "\n\nJika ada ketidaksesuaian pada data di atas, kamu dapat mengubah data kamu pada halaman profil."
                . "\nJangan khawatir, semua data kamu aman di JAGA."
                . "\nAkses JAGA dan bantu kami untuk Cegah Korupsi dari Ujung Jari."
                . "\n\nPerhatian: E-mail ini (termasuk seluruh lampirannya, bila ada) dikirim otomatis oleh aplikasi dan hanya ditujukan kepada penerima yang tercantum di atas. Jika Anda bukan penerima yang dituju, maka Anda tidak diperkenankan untuk memanfaatkan, menyebarkan, mendistribusikan, atau menggandakan e-mail ini beserta seluruh lampirannya. Mohon untuk tidak membalas email ini.";
        } else {
            $subject = "[JAGA] Perubahan Data Akun JAGA";
            $message = "Hi Sahabat JAGA, terima kasih telah menggunakan Aplikasi JAGA."
                . "\n\nKamu menerima email ini karena adanya perubahan data pada akun JAGA kamu. Berikut informasi perubahan data pada Akun kamu :"
                . "\n\n-Nama: " . $user->nama
                . "\n-Telepon: " . $user->phone
                . "\n-Email: " . $user->email
                . "\n\nJika ada ketidaksesuaian pada data di atas, kamu dapat mengubah data kamu pada halaman profil."
                . "\nJangan khawatir, semua data kamu aman di JAGA."
                . "\nAkses JAGA dan bantu kami untuk Cegah Korupsi dari Ujung Jari."
                . "\n\nPerhatian: E-mail ini (termasuk seluruh lampirannya, bila ada) dikirim otomatis oleh aplikasi dan hanya ditujukan kepada penerima yang tercantum di atas. Jika Anda bukan penerima yang dituju, maka Anda tidak diperkenankan untuk memanfaatkan, menyebarkan, mendistribusikan, atau menggandakan e-mail ini beserta seluruh lampirannya. Mohon untuk tidak membalas email ini.";
        }

        $mail = new MailHelper($this->config["mail_config"]);
        $mail->queue($user->email, $subject, $message, false, $this->shared->user->um_id);
    }
    #endregion
}
