<?php

use Phalcon\Mvc\Model\Transaction\Failed as TxFailed;
use Carbon\Carbon;
use Phalcon\Filter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

class DiskusiController extends BaseController
{
    public function kategoriProduk()
    {
        $params = $this->request->get();
        $params["produk"] = array_key_exists("produk", $params) ? $params["produk"] : 'all';

        $validatedRequest = $this->validate(
            $params,
            [
                'produk' => ['validators' => [
                    [
                        'name' => 'inclusion_in',
                        'params' => ['all', 'diskusi', 'keluhan']
                    ]
                ]],
            ]
        );

        if ($validatedRequest['produk'] == 'diskusi') {
            $resultset = Produk::find([
                'conditions' => 'id_index <> ?0 AND is_show = 1',
                'bind' => [9],
                "order" => "sort_order"
            ]);
        } else if ($validatedRequest['produk'] == 'keluhan') {
            $resultset = Produk::find([
                'conditions'  => 'id_index = ?0 AND is_show = 1',
                'bind' => [9],
                "order" => "sort_order"
            ]);
        } else {
            $resultset = Produk::find(
                [
                    "is_show = 1",
                    "order" => "sort_order"
                ]
            );
        }

        $result = [];

        foreach ($resultset as $product) {
            $result[] = $product->toArray(['id', 'id_index', 'nama', 'nama_en', 'category_id', 'produk_group']);
        };

        $totalResult = count($result);
        return new PaginatedResponse($result, $totalResult, $totalResult, 0);
    }

    public function subKategoriProduk()
    {
        $params = $this->request->get();
        $validatedRequest = $this->validate(
            $params,
            [
                'produk_id' => ['validators' => ['required']]
            ]
        );

        $validatedRequest = $this->validate(
            $params,
            [
                'produk_id' => ['validators' => ['digit']]
            ]
        );

        $result = KategoriDiskusi::find(
            [
                "columns" => "id, title",
                "produk_id = :produk_id:",
                "bind" => [
                    "produk_id" => $params["produk_id"]
                ]
            ]
        )
            ->toArray();
        $totalResult = count($result);
        return new PaginatedResponse($result, $totalResult, $totalResult, 0);
    }

    public function produkObjek()
    {
        $params = $this->request->get();
        $validatedRequest = $this->validate(
            $params,
            [
                'produk_id' => ['validators' => ['required']],
                'search' => ['validators' => ['required']]
            ]
        );

        $validatedRequest = $this->validate(
            $params,
            [
                'produk_id' => ['validators' => ['digit']]
            ]
        );

        $result = [];
        $totalResult = 0;
        if ($params["produk_id"] == 2) {
            $result = $this->searchSekolah($params["search"]);
            $totalResult = count($result);
        } elseif ($params["produk_id"] == 3) {
            $result = $this->searchRs($params["search"]);
            $totalResult = count($result);
        } elseif ($params["produk_id"] == 6) {
            $result = $this->searchDesa($params["search"]);
            $totalResult = count($result);
        } elseif ($params['produk_id'] == 5) {
            $result = $this->searchPerizinan($params["search"]);
            $totalResult = count($result);
        } elseif ($params['produk_id'] == 9 || $params['produk_id'] == 16) {
            $result = $this->searchInstansi($params["search"]);
            $totalResult = count($result);
        } elseif (in_array($params['produk_id'], [10, 11, 12, 13, 14, 21, 22, 23])) {
            $result = $this->searchWilayah($params["search"]);
            $totalResult = count($result);
        } elseif ($params['produk_id'] == 15) {
            $result = $this->searchKampus($params["search"]);
            $totalResult = count($result);
        } elseif ($params['produk_id'] == 20) {
            $result = $this->searchPelabuhan($params["search"]);
            $totalResult = count($result);
        }

        return new PaginatedResponse($result, $totalResult, $totalResult, 0);
    }

    public function allProdukObjek()
    {
        $params = $this->request->get();
        $validatedRequest = $this->validate(
            $params,
            [
                'search' => ['validators' => ['required']]
            ]
        );

        $result = $this->rawQuery("
            SELECT id, nama, tipe
            FROM jaga.view_produk_diskusi
            WHERE lower(nama) like :nama
            LIMIT 10
            OFFSET 0
            ", [
            'nama' => CommonTools::getDummyFuzzyKeyword($validatedRequest['search'])
        ]);
        $totalResult = count($result);

        return new PaginatedResponse($result, $totalResult, $totalResult, 0);
    }

    private function searchPerizinan($nama)
    {
        return Perizinan::query()
            ->columns('id, nama')
            ->where("lower(nama) like :nama:")
            ->bind(
                [
                    "nama" => CommonTools::getDummyFuzzyKeyword($nama)
                ]
            )
            ->limit(10, 0)
            ->execute()
            ->toArray();
    }

    private function searchSekolah($namaSekolah)
    {
        return Sekolah::query()
            ->columns('npsn as id, nama, MasterKotaKabupaten.kota_kabupaten_name as kota')
            ->join('MasterKecamatan', 'Sekolah.kecamatan_code = MasterKecamatan.kecamatan_code')
            ->join('MasterKotaKabupaten', 'MasterKotaKabupaten.kota_kabupaten_code=MasterKecamatan.kota_kabupaten_code')
            ->where("lower(nama) like :nama:")
            ->bind(
                [
                    "nama" => CommonTools::getDummyFuzzyKeyword($namaSekolah)
                ]
            )
            ->limit(10, 0)
            ->execute()
            ->toArray();
    }

    private function searchRs($nama)
    {
        return RumahSakitOnline::query()
            ->columns("kode as id, nama")
            ->where("lower(nama) like :nama:")
            ->bind(
                [
                    "nama" => CommonTools::getDummyFuzzyKeyword($nama)
                ]
            )
            ->limit(10, 0)
            ->execute()
            ->toArray();
    }


    private function searchPuskesmas($nama)
    {
        return Puskesmas::query()
            ->columns("kode_puskesmas as id, nama_puskesmas as nama")
            ->where("lower(nama_puskesmas) like :nama:")
            ->bind(
                [
                    "nama" => CommonTools::getDummyFuzzyKeyword($nama)
                ]
            )
            ->limit(10, 0)
            ->execute()
            ->toArray();
    }

    private function searchDesa($nama)
    {
        return OmspanMasterDesa::query()
            ->columns("kode_desa as id, nama_desa as nama")
            ->where("lower(nama_desa) like :nama:")
            ->bind(
                [
                    "nama" => CommonTools::getDummyFuzzyKeyword($nama)
                ]
            )
            ->limit(10, 0)
            ->execute()
            ->toArray();
    }

    private function searchInstansi($nama)
    {
        return Instansi::query()
            ->columns("id, nama")
            ->where("lower(nama) like :nama: and id_provinsi is not null")
            ->bind(
                [
                    "nama" => CommonTools::getDummyFuzzyKeyword($nama)
                ]
            )
            ->limit(10, 0)
            ->execute()
            ->toArray();
    }

    private function searchWilayah($nama)
    {
        return MasterWilayah::query()
            ->columns("id, name as nama")
            ->where("lower(name) like :nama: and active = 1")
            ->bind(
                [
                    "nama" => CommonTools::getDummyFuzzyKeyword($nama)
                ]
            )
            ->limit(10, 0)
            ->execute()
            ->toArray();
    }

    private function searchKampus($nama)
    {
        return PerguruanTinggi::query()
            ->columns("id_pt as id, nama")
            ->where("lower(nama) like :nama: and deleted_at IS NULL")
            ->bind(
                [
                    "nama" => CommonTools::getDummyFuzzyKeyword($nama)
                ]
            )
            ->limit(10, 0)
            ->execute()
            ->toArray();
    }

    private function searchPelabuhan($search)
    {
        $instansiTipePelabuhan = InstansiTipe::findFirst([
            'conditions' => 'nama = ?0',
            'bind' => [
                'Pelabuhan'
            ]
        ]);

        if (empty($instansiTipePelabuhan)) {
            throw new ValidationException("Instansi Tipe Pelabuhan tidak ditemukan");
        }

        $idTipe = $instansiTipePelabuhan->id;

        return Instansi::query()
            ->columns("id, nama")
            ->where("id_tipe = :id_tipe: AND nama ILIKE :nama: AND deleted_at IS NULL")
            ->bind([
                'id_tipe' => $idTipe,
                'nama' => CommonTools::getDummyFuzzyKeyword($search)
            ])
            ->limit(10, 0)
            ->execute()
            ->toArray();
    }

    public function submitCerita()
    {
        return $this->submit(1);
    }

    public function submitDiskusi()
    {
        return $this->submit(0);
    }

    private function submit($type)
    {
        try {
            $this->db->begin();

            date_default_timezone_set('Asia/Jakarta');
            $this->checkScopes(['discussion.create']);

            $params = $this->request->getPost();

            $validatedRequest = null;
            if ($type == 1) {
                $validatedRequest = $this->validate(
                    $params,
                    [
                        'produk_id' => ['validators' => ['required', 'digit']],
                        'judul' => ['validators' => ['required', [
                            'name' => 'max_string',
                            'params' => 255
                        ]]],
                        'judul_en' => ['validators' => [[
                            'name' => 'max_string',
                            'params' => 255
                        ]]],
                        'is_anonymous' => ['validators' => ['required', 'digit']],
                        'konten_diskusi' => ['validators' => ['required']],
                        'konten_diskusi_en' => ['validators' => []],
                        'link_terkait' => ['validators' => []],
                        'objek_id' => ['validators' => []],
                        'release_date' => ['validators' => ['datetime']],
                        'nama_objek' => ['validators' => []],
                        'tags' => ['validators' => []],
                        'captions' => ['validators' => []],
                        'captions_en' => ['validators' => []],
                    ]
                );

                Diskusi::validateJudulBerita($validatedRequest["judul"]);

                if (!empty($validatedRequest['tags']) && $validatedRequest['tags'] != null) {
                    $tags = json_decode($validatedRequest['tags'], true);
                    foreach ($tags as $tag) {
                        $this->validate(
                            $tag,
                            [
                                'label' => [
                                    'validators' => [
                                        [
                                            'name' => 'max_string',
                                            'params' => 30
                                        ]
                                    ]
                                ]
                            ]
                        );
                    }
                }
            } else {
                $validatedRequest = $this->validate(
                    $params,
                    [
                        'produk_id' => ['validators' => ['required', 'digit']],
                        'judul' => ['validators' => ['required'], [
                            'name' => 'max_string',
                            'params' => 255
                        ]],
                        'is_anonymous' => ['validators' => ['required', 'digit']],
                        'konten_diskusi' => ['validators' => ['required']],
                        'link_terkait' => ['validators' => []],
                        'objek_id' => ['validators' => []],
                        'nama_objek' => ['validators' => []],
                        'mentions' => ['validators' => []]
                    ]
                );
            }

            $service = new DiskusiService($this->config, $this->shared, $this->request, $this->minio);

            $result = $service->create($validatedRequest, $type);

            if ($result['res'] && $result['saveRespon'] && $result['saveDiskusi']) {
                $this->db->commit();

                if ($type == 1) {
                    DiskusiService::generateSitemap($this->logger, $this->config['application']['publicDir']);
                } else {
                    $service->notifyStakeHolder($result['diskusi'], $result['produk'], $validatedRequest, $this->logger);
                }

                return new ObjectResponse(
                    [
                        "success" => true,
                        "data" => [
                            "diskusi_uuid" => $result['diskusi']->uuid,
                            "diskusi_id" => $result['diskusi']->id
                        ]
                    ]
                );
            } else {
                if (!$result['res']) {
                    throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Create Diskusi Error", $result['diskusi']->getMessages());
                }

                if (!$result['saveRespon']) {
                    throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Create Diskusi Response Error", $result['diskusi']->getMessages());
                }

                if (!$result['saveDiskusi']) {
                    throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Create Diskusi Detail Error", $result['diskusi']->getMessages());
                }

                throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "SQL Error", $result['diskusi']->getMessages());
            }
        } catch (Exception $e) {
            $this->db->rollback();
            throw $e;
        }
    }


    public function diskusiMention()
    {
        $result = array();
        $totalResult = 0;
/*        if ($this->isAuthenticated()) {
            $result = UsersRoles::query()
                ->columns("distinct user_id, UmUser.um_user_name as nama, '0' as type, UmUser.uuid_user as user_uuid")
                ->join('UmUser', 'UsersRoles.user_id = UmUser.um_id')
                ->where("(role_id = 2 or role_id = 3) and UmUser.um_id != :my_id:")
                ->bind(
                    [
                        "my_id" => $this->shared->user->um_id
                    ]
                )
                ->execute()
                ->toArray();
            $totalResult = count($result);
        }*/
        return new PaginatedResponse($result, $totalResult, $totalResult, 0);
    }

    public function diskusiKomentarMention()
    {
//        $params = $this->request->get();
//        $this->validate(
//            $params,
//            [
//                'diskusi_id' => ['validators' => ['required', 'digit']]
//            ]
//        );
//
//        if (isset($this->shared->user)) {
//            $result = UsersRoles::query()
//                ->columns("distinct user_id, UmUser.um_user_name as nama, '0' as type, UmUser.uuid_user as user_uuid")
//                ->join('UmUser', 'UsersRoles.user_id = UmUser.um_id')
//                ->where("(role_id = 2 or role_id = 3) and UmUser.um_id != :my_id:")
//                ->bind(
//                    [
//                        "my_id" => $this->shared->user->um_id
//                    ]
//                )
//                ->execute()
//                ->toArray();
//        } else {
//            $result = UsersRoles::query()
//                ->columns("distinct user_id, UmUser.um_user_name as nama, '0' as type, UmUser.uuid_user as user_uuid")
//                ->join('UmUser', 'UsersRoles.user_id = UmUser.um_id')
//                ->where("(role_id = 2 or role_id = 3)")
//                ->execute()
//                ->toArray();
//        }
//
//        $crit = "DiskusiComment.diskusi_id = :diskusi_id: and 1 = CASE Diskusi.user_id WHEN DiskusiComment.user_id THEN CASE Diskusi.is_anonymous WHEN 0 THEN 1 ELSE 0 END ELSE 1 END";
//        $bind = ["diskusi_id" => $params["diskusi_id"]];
//        if (isset($this->shared->user)) {
//            $crit .= " and DiskusiComment.user_id <> :user_id:";
//            $bind["user_id"] = $this->shared->user->um_id;
//        }
//        $resKomentator = DiskusiComment::query()
//            ->columns("distinct DiskusiComment.user_id, UmUser.um_user_name as nama, '0' as type, UmUser.uuid_user as user_uuid")
//            ->join('UmUser', 'DiskusiComment.user_id = UmUser.um_id')
//            ->join('Diskusi', 'Diskusi.id = DiskusiComment.diskusi_id')
//            ->where($crit)
//            ->bind($bind)
//            ->execute()
//            ->toArray();
//        $res = array_merge($result, $resKomentator);
//        $res = array_values(array_unique($res, SORT_REGULAR));
//        $totalResult = count($res);
//        return new PaginatedResponse($res, $totalResult, $totalResult, 0);
        return new CollectionPaginatedResponse([]);
    }

    public function diskusiHashtag()
    {
        $result = DiskusiHashtag::query()
            ->columns("distinct user_id, hashtag as nama, '1' as type, UmUser.uuid_user as user_uuid")
            ->join('UmUser', 'DiskusiHashtag.user_id = UmUser.um_id')
            ->execute()
            ->toArray();

        $totalResult = count($result);
        return new PaginatedResponse($result, $totalResult, $totalResult, 0);
    }

    private function sendNotif($userId, $senderUserId, $body, $action, $actionId, $bodyEn)
    {
        $clickAction = $this->getClickAction($action, $actionId);

        NotificationHelper::send($userId, $senderUserId, $body, $action, $actionId, $clickAction, NotificationHelper::CATEGORY_DISCUSSION, $bodyEn);
    }

    // 0 : terbaru
    // 1 : terlama
    // 2 : share terbanyak
    // 3 : komentar terbanyak
    // 4 : like terbanyak
    private function doGetKumpulanDiskusi($type)
    {
        $params = $this->request->get();

        $params["produk_id"] = array_key_exists("produk_id", $params) ? $params["produk_id"] : 0;
        $params["kategori_produk_id"] = array_key_exists("kategori_produk_id", $params) ? $params["kategori_produk_id"] : 0;
        $params['limit'] = $this->getDefaultLimit($params);
        $params["offset"] = array_key_exists("offset", $params) ? $params["offset"] : 0;
        $params["objek_id"] = array_key_exists("objek_id", $params) && strlen($params["objek_id"]) > 0 ? $params["objek_id"] : -1;
        $params["objek_nama"] = array_key_exists("objek_nama", $params) && strlen($params["objek_nama"]) > 0 ? $params["objek_nama"] : null;
        $params["sort"] = array_key_exists("sort", $params) && strlen($params["sort"]) > 0 ? $params["sort"] : -1;
        $params["is_banned"] = array_key_exists("is_banned", $params) ? $params["is_banned"] : false;
        $params["id_topik"] = array_key_exists("id_topik", $params) ? $params["id_topik"] : -1;
        $params["title"] = array_key_exists("title", $params) && strlen($params["title"]) > 0 ? $params["title"] : null;
        $params["topik_type"] = array_key_exists("topik_type", $params) && strlen($params["topik_type"]) > 0 ? $params["topik_type"] : null;
        $params["tags"] = array_key_exists("tags", $params) ? $params["tags"] : null;
        $params["id_instansi"] = array_key_exists("id_instansi", $params) && strlen($params["id_instansi"]) > 0 ? $params["id_instansi"] : -1;
        $params['produk'] = array_key_exists('produk', $params) ? $params['produk'] : 'all';

        $validatedRequest = $this->validate(
            $params,
            [
                'produk_id' => ['validators' => ['digit']],
                'kategori_produk_id' => ['validators' => ['digit']],
                'limit' => ['validators' => ['digit']],
                'offset' => ['validators' => ['digit']],
                'sort' => ['validators' => ['numeric']],
                'tags' => ['validators' => []],
                'produk' => ['validators' => [
                    [
                        'name' => 'inclusion_in',
                        'params' => ['all', 'diskusi', 'keluhan']
                    ]
                ]]
            ]
        );

        $query = Diskusi::query()
            ->columns("Diskusi.id, Diskusi.uuid, Diskusi.title, Diskusi.title_en, Diskusi.content, Diskusi.content_en, Diskusi.created_at, Diskusi.updated_at, Diskusi.release_date, Diskusi.user_id, UmUser.is_banned, UmUser.um_user_name as nama, UmUser.jenis_kelamin, Produk.nama as nama_produk, foto, is_anonymous, (select count(*) from DiskusiComment x where x.diskusi_id = Diskusi.id) as total_comment, (select count(*) from DiskusiShare x where x.diskusi_id = Diskusi.id) as total_share, (select count(*) from DiskusiLike x where x.diskusi_id = Diskusi.id) as total_like, TopikDiskusiBansos.type as topik_type, UmUser.checkmark_verified, UmUser.presigned_url, UmUser.presigned_exp ")
            ->join('UmUser', 'Diskusi.user_id = UmUser.um_id')
            ->join('KategoriDiskusi', 'Diskusi.kategori_diskusi_id = KategoriDiskusi.id')
            ->join('Produk', 'KategoriDiskusi.produk_id = Produk.id_index')
            ->leftjoin('DiskusiObjek', 'Diskusi.id = DiskusiObjek.diskusi_id')
            ->leftjoin('DiskusiBansos', 'Diskusi.id = DiskusiBansos.id_diskusi AND DiskusiBansos.deleted_at IS NULL')
            ->leftjoin('TopikDiskusiBansos', 'DiskusiBansos.id_topik = TopikDiskusiBansos.id');

        $queryCount = Diskusi::query()
            ->columns("count(1) as jumlah")
            ->join('UmUser', 'Diskusi.user_id = UmUser.um_id')
            ->join('KategoriDiskusi', 'Diskusi.kategori_diskusi_id = KategoriDiskusi.id')
            ->join('Produk', 'KategoriDiskusi.produk_id = Produk.id_index')
            ->leftjoin('DiskusiObjek', 'Diskusi.id = DiskusiObjek.diskusi_id')
            ->leftjoin('DiskusiBansos', 'Diskusi.id = DiskusiBansos.id_diskusi AND DiskusiBansos.deleted_at IS NULL')
            ->leftjoin('TopikDiskusiBansos', 'DiskusiBansos.id_topik = TopikDiskusiBansos.id');

        if ($params['kategori_produk_id'] > 0) {
            $strWhere = "Produk.category_id = :kategori_produk_id:";

            $bind = [
                "kategori_produk_id" => $params["kategori_produk_id"]
            ];
        } elseif ($params["produk_id"] == 0) {
            $strWhere = "Produk.id_index != :produk_id: and Produk.is_active = TRUE";

            $bind = [
                "produk_id" => $params["produk_id"]
            ];
        } else {
            $strWhere = "Produk.id_index = :produk_id:";

            $bind = [
                "produk_id" => $params["produk_id"]
            ];
        }

        $strWhere .= " and Diskusi.type = " . $type;
        $strWhere .= " and is_deleted = 0 ";

        if ($params["objek_id"] != -1) {
            $strWhere .= " and DiskusiObjek.objek_id = :objek_id:";
            $bind["objek_id"] = $params["objek_id"];
            if (!empty($params["objek_nama"])) {
                $strWhere .= " and DiskusiObjek.nama_objek = :objek_nama:";
                $bind["objek_nama"] = $params["objek_nama"];
            }
        }

        if ($params["id_topik"] != -1) {
            $strWhere .= " and DiskusiBansos.id_topik = :id_topik:";
            $bind["id_topik"] = $params["id_topik"];
        }

        if ($params["is_banned"]) {
            $strWhere .= " and UmUser.is_banned = :ban:";
            $bind['ban'] = $params['is_banned'];
        }

        if ($params["title"]) {
            $strWhere .= " and (Diskusi.title ilike :title: or Diskusi.title_en ilike :title:)";
            $bind['title'] = '%' . $params['title'] . '%';
        }

        if ($params['topik_type']) {
            $strWhere .= " AND TopikDiskusiBansos.type = :topik_type:";
            $bind['topik_type'] = $params['topik_type'];
        }

        if ($params["id_instansi"] != -1) {
            $strWhere .= " and DiskusiBansos.id_instansi_origin = :id_instansi:";
            $bind["id_instansi"] = $params["id_instansi"];
        }

        if ($params["produk"] != 'all') {
            if ($params['produk'] == 'diskusi') {
                $strWhere .= " AND Produk.id_index <> :id_index: ";
                $bind["id_index"] = 9;
            } else if ($params['produk'] == 'keluhan') {
                $strWhere .= " AND Produk.id_index = :id_index: ";
                $bind["id_index"] = 9;

                $strWhere .= " AND (TopikDiskusiBansos.type <> :bansos: AND TopikDiskusiBansos.type <> :covid:)";
                $bind["bansos"] = 'BANSOS';
                $bind["covid"] = 'COVID';
            }
        }

        if ($type == 1) {
            try {
                $tz = 'Asia/Jakarta';
                $timestamp = time();
                $dt = new DateTime("now", new DateTimeZone($tz));
                $dt->setTimestamp($timestamp);
                $strWhere .= " and (release_date is not null and release_date <= '" . $dt->format('Y-m-d H:i:s') . "')";
            } catch (Exception $e) {
                $strWhere .= " and (release_date is not null and release_date <= '" . date("Y-m-d H:i:s") . "')";
            }
        }

        if ($params["sort"] == 0) {
            if ($type == 1) {
                $orderBy = "Diskusi.release_date desc";
            } else {
                $orderBy = "Diskusi.updated_at desc";
            }
        } elseif ($params["sort"] == 1) {
            if ($type == 1) {
                $orderBy = "Diskusi.release_date asc";
            } else {
                $orderBy = "Diskusi.updated_at asc";
            }
        } elseif ($params["sort"] == 2) {
            $orderBy = "total_share desc";
        } elseif ($params["sort"] == 3) {
            $orderBy = "total_comment desc";
        } elseif ($params["sort"] == 4) {
            $orderBy = "total_like desc, Diskusi.title desc";
        } else {
            if ($type == 1) {
                $orderBy = "Diskusi.release_date desc";
            } else {
                $orderBy = "Diskusi.updated_at desc";
            }
        }

        if (!empty($params['tags']) && $params['tags'] != null && $type == '1') {
            $tags = explode('|', $params['tags']);
            $idDikusi = array_column(
                BeritaTags::query()
                    ->where('id_tags in ({idtags:array})')
                    ->bind([
                        'idtags' => array_values($tags)
                    ])
                    ->execute()
                    ->toArray(),
                'id_diskusi'
            );
            if (empty($idDikusi)) {
                $strWhere .= " AND FALSE"; //artinya tidak ada diskusi dengan tags tersebut
            } else {
                $strWhere .= " AND Diskusi.id in ({iddiskusi:array})";
                $bind['iddiskusi'] = array_values($idDikusi);
            }
        }

        $result = $query
            ->where($strWhere)
            ->bind($bind)
            ->limit($params["limit"], $params["offset"])
            ->orderBy($orderBy)
            ->execute()
            ->toArray();

        $contentLength = 500;
        $filter = new Filter();

        for ($i = 0; $i < count($result); $i++) {
            $result[$i]["profile_picture_url"] = $result[$i]["is_anonymous"] == 1 || $result[$i]["foto"] == null || $result[$i]["foto"] == '' || $result[$i]["foto"] == '-' ? null : MediaHelper::getPresignedUrlFoto($result[$i], UmUser::class, $this->config, $this->minio);
            unset($result[$i]["presigned_url"]);
            unset($result[$i]["presigned_exp"]);

            if ($result[$i]["is_anonymous"] == 1) {
                if ($type == 1) $result[$i]["nama"] = "Tim JAGA KPK";
                else $result[$i]["nama"] = "Anonim";
            }

            $result[$i]["content"] = $filter->sanitize($result[$i]["content"], 'striptags');

            $censorResult  = $this->getCensoredContent($result[$i]["content"]);
            if ($censorResult['is_censored']) {
                $result[$i]["content"] = $censorResult['censored_content'];
            }
            $result[$i]['is_censored'] = $censorResult['is_censored'];

            if (strlen($result[$i]["content"]) >= $contentLength) {
                $result[$i]["content"] = mb_substr($result[$i]["content"], 0, ($contentLength - 3)) . "...";
            }

            if ($type == 1) {
                $imageUuid = DiskusiAttachment::find(
                    [
                        "columns" => "uuid",
                        "diskusi_id = :diskusi_id: and filetype ilike '%image%'",
                        "bind" => [
                            "diskusi_id" => $result[$i]["id"]
                        ],
                        "limit" => 1
                    ]
                )
                    ->toArray();

                if (count($imageUuid) == 1) {
                    $result[$i]["image_uuid"] = $imageUuid[0]["uuid"];
                }

                $diskusi = new Diskusi();
                $diskusi->assign($result[$i]);
                $result[$i]['tags'] = $diskusi->tags->toArray();

                $result[$i]['slug'] = CommonTools::slug($result[$i]['title']);
            }

            $report = Diskusi::getReport($result[$i]['id']);
            $reportUser = DiskusiLapor::findFirst([
                'status=:status: AND id_user_terlapor =:id_terlapor: AND deleted_at IS NULL',
                'bind' => [
                    'id_terlapor' => $result[$i]['user_id'],
                    'status' => 1
                ]
            ]);

            if (empty($reportUser) && !$result[$i]['is_banned']) {
                $result[$i]['laporan_user'] = null;
            } else {
                /*$result[$i]['laporan_user'] = $reportUser;*/
                $result[$i]['title'] = '-';
                $result[$i]['title_en'] = '-';
                $result[$i]['content'] = '-';
                $result[$i]['content_en'] = '-';
                $result[$i]['is_anonymous'] = 0;
            }

            if (empty($report)) {
                /*$result[$i]['laporan'] = null;*/
            } else {
                /*$result[$i]['laporan'] = $report->simpleToArray();*/
                $result[$i]['title'] = '-';
                $result[$i]['title_en'] = '-';
                $result[$i]['content'] = '-';
                $result[$i]['content_en'] = '-';
                $result[$i]['is_anonymous'] = 0;
            }

            $result[$i]["count_like"] = $result[$i]["total_like"];
            $result[$i]["count_share"] = $result[$i]["total_share"];
            $result[$i]["count_comment"] = $result[$i]["total_comment"];

            unset($result[$i]["is_anonymous"]);
            unset($result[$i]["is_censored"]);
            unset($result[$i]["is_banned"]);
        }

        $totalResult = $queryCount
            ->where($strWhere)
            ->bind($bind)
            ->execute()
            ->toArray()[0]["jumlah"];

        return new PaginatedResponse($result, $totalResult, $params["limit"], $params["offset"]);
    }

    public function ceritaSukses()
    {
        return $this->doGetKumpulanDiskusi(1);
    }

    public function kumpulanDiskusi()
    {
        return $this->doGetKumpulanDiskusi(0);
    }

    public function kumpulanMascotDesign()
    {
        $params = $this->request->get();

        $params['limit'] = $this->getDefaultLimit($params);
        $params["offset"] = array_key_exists("offset", $params) ? $params["offset"] : 0;
        $params["sort"] = array_key_exists("sort", $params) && strlen($params["sort"]) > 0 ? $params["sort"] : -1;

        $validatedRequest = $this->validate(
            $params,
            [
                'limit' => ['validators' => ['digit']],
                'offset' => ['validators' => ['digit']],
                'sort' => ['validators' => ['numeric']]
            ]
        );

        $query = Diskusi::query()
            ->columns("Diskusi.id, Diskusi.uuid, Diskusi.title, Diskusi.content, Diskusi.created_at, Diskusi.updated_at, Diskusi.release_date, Diskusi.user_id, UmUser.is_banned, UmUser.um_user_name as nama, UmUser.uuid_user, UmUser.jenis_kelamin, Produk.nama as nama_produk, Diskusi.is_deleted, foto, is_anonymous, (select count(*) from DiskusiComment x where x.diskusi_id = Diskusi.id) as total_comment, (select count(*) from DiskusiShare x where x.diskusi_id = Diskusi.id) as total_share, (select count(*) from DiskusiLike x where x.diskusi_id = Diskusi.id) as total_like, UmUser.um_id, UmUser.presigned_url, UmUser.presigned_exp ")
            ->join('UmUser', 'Diskusi.user_id = UmUser.um_id')
            ->join('KategoriDiskusi', 'Diskusi.kategori_diskusi_id = KategoriDiskusi.id')
            ->join('Produk', 'KategoriDiskusi.produk_id = Produk.id_index');

        $queryCount = Diskusi::query()
            ->columns("count(1) as jumlah")
            ->join('UmUser', 'Diskusi.user_id = UmUser.um_id')
            ->join('KategoriDiskusi', 'Diskusi.kategori_diskusi_id = KategoriDiskusi.id')
            ->join('Produk', 'KategoriDiskusi.produk_id = Produk.id_index');

        $strWhere = "Diskusi.kategori_diskusi_id = :kategori_produk_id: and Diskusi.type = 0 and Diskusi.is_deleted = 0";

        $bind = [
            "kategori_produk_id" => getenv("PRODUK_MASCOT_CHALLENGE_ID")
        ];

        if ($params["sort"] == 0) {
            $orderBy = "Diskusi.updated_at desc";
        } elseif ($params["sort"] == 1) {
            $orderBy = "Diskusi.updated_at asc";
        } elseif ($params["sort"] == 2) {
            $orderBy = "total_share desc";
        } elseif ($params["sort"] == 3) {
            $orderBy = "total_comment desc";
        } elseif ($params["sort"] == 4) {
            $orderBy = "total_like desc, Diskusi.title asc";
        } else {
            $orderBy = "Diskusi.updated_at desc";
        }

        $result = $query
            ->where($strWhere)
            ->bind($bind)
            ->limit($params["limit"], $params["offset"])
            ->orderBy($orderBy)
            ->execute()
            ->toArray();

        $contentLength = 500;
        $filter = new Filter();

        for ($i = 0; $i < count($result); $i++) {
            $result[$i]["profile_picture_url"] = $result[$i]["is_anonymous"] == 1 || $result[$i]["foto"] == null || $result[$i]["foto"] == '' || $result[$i]["foto"] == '-' ? null : MediaHelper::getPresignedUrlFoto($result[$i], UmUser::class, $this->config, $this->minio);
            unset($result[$i]["presigned_url"]);
            unset($result[$i]["presigned_exp"]);
            if ($result[$i]["is_anonymous"] == 1) {
                $result[$i]["nama"] = "Anonim";
                /*$result[$i]["profile_picture_url"] = "";*/
            }

            $result[$i]["content"] = $filter->sanitize($result[$i]["content"], 'striptags');
            if (strlen($result[$i]["content"]) >= $contentLength) {
                $result[$i]["content"] = mb_substr($result[$i]["content"], 0, ($contentLength - 3)) . "...";
            }

            $censorResult  = $this->getCensoredContent($result[$i]["content"]);
            if ($censorResult['is_censored']) {
                $result[$i]["content"] = $censorResult['censored_content'];
            }
            $result[$i]['is_censored'] = $censorResult['is_censored'];

            $obj = new Diskusi();
            $obj->assign($result[$i]);
            foreach ($obj->diskusiAttachment as $key => $attachment) {
                $result[$i]['attachments'][] = [
                    'uuid' => $attachment->uuid,
                    "filename" => $attachment->filename,
                    "filetype" => $attachment->filetype
                ];
            }

            $diskusi_id = $result[$i]["id"];
            $result[$i]['count_like'] = DiskusiLike::find("diskusi_id='$diskusi_id'")->count();
            $result[$i]['count_comment'] = DiskusiComment::find("diskusi_id='$diskusi_id'")->count();
            $result[$i]['count_share'] = DiskusiShare::find("diskusi_id='$diskusi_id'")->count();
        }

        $totalResult = $queryCount
            ->where($strWhere)
            ->bind($bind)
            ->execute()
            ->toArray()[0]["jumlah"];

        return new PaginatedResponse($result, $totalResult, $params["limit"], $params["offset"]);
    }

    // sort
    // 0 : terbaru
    // 1 : terlama
    // 2 : share terbanyak
    // 3 : komentar terbanyak
    // 4 : like terbanyak
    public function diskusiku()
    {
        $this->checkIsAuthenticated();

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

        $params["produk_id"] = array_key_exists("produk_id", $params) ? $params["produk_id"] : 0;
        $params["kategori_produk_id"] = array_key_exists("kategori_produk_id", $params) ? $params["kategori_produk_id"] : 0;
        $params['limit'] = $this->getDefaultLimit($params);
        $params["offset"] = array_key_exists("offset", $params) ? $params["offset"] : 0;
        $params["objek_id"] = array_key_exists("objek_id", $params) && strlen($params["objek_id"]) > 0 ? $params["objek_id"] : -1;
        $params["objek_nama"] = array_key_exists("objek_nama", $params) && strlen($params["objek_nama"]) > 0 ? $params["objek_nama"] : null;
        $params["sort"] = array_key_exists("sort", $params) && strlen($params["sort"]) > 0 ? $params["sort"] : -1;
        $params["title"] = array_key_exists("title", $params) && strlen($params["title"]) > 0 ? $params["title"] : null;
        $params['topik_type'] = array_key_exists('topik_type', $params) ? $params['topik_type'] : null;
        $params['produk'] = array_key_exists('produk', $params) ? $params['produk'] : 'all';

        $validatedRequest = $this->validate(
            $params,
            [
                'produk_id' => ['validators' => ['digit']],
                'kategori_produk_id' => ['validators' => ['digit']],
                'limit' => ['validators' => ['digit']],
                'offset' => ['validators' => ['digit']],
                'sort' => ['validators' => ['numeric']],
                'produk' => ['validators' => [
                    [
                        'name' => 'inclusion_in',
                        'params' => ['all', 'diskusi', 'keluhan']
                    ]
                ]]
            ]
        );

        if ($params['kategori_produk_id'] > 0) {
            $strWhere = "Produk.category_id = :kategori_produk_id:";

            $bind = [
                "kategori_produk_id" => $params["kategori_produk_id"]
            ];
        } elseif ($params["produk_id"] == 0) {
            $strWhere = "Produk.id_index != :produk_id:";

            $bind = [
                "produk_id" => $params["produk_id"]
            ];
        } else {
            $strWhere = "Produk.id_index = :produk_id:";

            $bind = [
                "produk_id" => $params["produk_id"]
            ];
        }

        if ($params["title"]) {
            $strWhere .= " and Diskusi.title ilike :title:";
            $bind['title'] = '%' . $params['title'] . '%';
        }

        if ($params['topik_type']) {
            $strWhere .= " AND TopikDiskusiBansos.type = :topik_type:";
            $bind['topik_type'] = $params['topik_type'];
        }

        if ($params['produk'] != 'all') {
            if ($params['produk'] == 'diskusi') {
                $strWhere .= " AND Produk.id_index <> :id_index: ";
            } else if ($params['produk'] == 'keluhan') {
                $strWhere .= " AND Produk.id_index = :id_index: ";

                $strWhere .= " AND (TopikDiskusiBansos.type <> :bansos: AND TopikDiskusiBansos.type <> :covid:)";
                $bind["bansos"] = 'BANSOS';
                $bind["covid"] = 'COVID';
            }
            $bind["id_index"] = 9;
        }

        $strWhere .= " and Diskusi.user_id = :user_id:";
        $strWhere .= " and Diskusi.type = 0";
        $bind['user_id'] = $this->shared->user->um_id;

        $query = Diskusi::query()
            ->columns("Diskusi.id, Diskusi.uuid, Diskusi.title, Diskusi.content, Diskusi.created_at, Diskusi.updated_at, UmUser.um_user_name as nama, UmUser.uuid_user, UmUser.jenis_kelamin, Produk.nama as nama_produk, Diskusi.is_deleted, foto, (select count(*) from DiskusiComment x where x.diskusi_id = Diskusi.id) as total_comment, (select count(*) from DiskusiShare x where x.diskusi_id = Diskusi.id) as total_share, (select count(*) from DiskusiLike x where x.diskusi_id = Diskusi.id) as total_like, TopikDiskusiBansos.type as topik_type, UmUser.checkmark_verified, UmUser.um_id, UmUser.presigned_url, UmUser.presigned_exp")
            ->join('UmUser', 'Diskusi.user_id = UmUser.um_id')
            ->join('KategoriDiskusi', 'Diskusi.kategori_diskusi_id = KategoriDiskusi.id')
            ->join('Produk', 'KategoriDiskusi.produk_id = Produk.id_index')
            ->leftjoin('DiskusiBansos', 'Diskusi.id = DiskusiBansos.id_diskusi')
            ->leftjoin('TopikDiskusiBansos', 'DiskusiBansos.id_topik = TopikDiskusiBansos.id');

        $queryCount = Diskusi::query()
            ->columns("count(1) as jumlah")
            ->join('UmUser', 'Diskusi.user_id = UmUser.um_id')
            ->join('KategoriDiskusi', 'Diskusi.kategori_diskusi_id = KategoriDiskusi.id')
            ->join('Produk', 'KategoriDiskusi.produk_id = Produk.id_index')
            ->leftjoin('DiskusiBansos', 'Diskusi.id = DiskusiBansos.id_diskusi')
            ->leftjoin('TopikDiskusiBansos', 'DiskusiBansos.id_topik = TopikDiskusiBansos.id');

        if ($params["objek_id"] != -1) {
            $query = $query->join("DiskusiObjek", "DiskusiObjek.diskusi_id = Diskusi.id");
            $queryCount = $queryCount->join("DiskusiObjek", "DiskusiObjek.diskusi_id = Diskusi.id");
            $strWhere .= " and DiskusiObjek.objek_id = :objek_id:";
            $bind["objek_id"] = $params["objek_id"];
            if (!empty($params["objek_nama"])) {
                $strWhere .= " and DiskusiObjek.nama_objek = :objek_nama:";
                $bind["objek_nama"] = $params["objek_nama"];
            }
        }

        if ($params["sort"] == 0) {
            $orderBy = "Diskusi.updated_at desc";
        } elseif ($params["sort"] == 1) {
            $orderBy = "Diskusi.updated_at asc";
        } elseif ($params["sort"] == 2) {
            $orderBy = "total_share desc";
        } elseif ($params["sort"] == 3) {
            $orderBy = "total_comment desc";
        } elseif ($params["sort"] == 4) {
            $orderBy = "total_like desc";
        } else {
            $orderBy = "Diskusi.updated_at desc";
        }

        $result = $query
            ->where($strWhere)
            ->bind($bind)
            ->limit($params["limit"], $params["offset"])
            ->orderBy($orderBy)
            ->execute()
            ->toArray();

        for ($i = 0; $i < count($result); $i++) {
            $result[$i]["profile_picture_url"] = $result[$i]["foto"] == null || $result[$i]["foto"] == '' || $result[$i]["foto"] == '-' ? null : MediaHelper::getPresignedUrlFoto($result[$i], UmUser::class, $this->config, $this->minio);
            unset($result[$i]["presigned_url"]);
            unset($result[$i]["presigned_exp"]);

            $diskusi_id = $result[$i]["id"];
            $result[$i]['count_like'] = DiskusiLike::find("diskusi_id='$diskusi_id'")->count();
            $result[$i]['count_comment'] = DiskusiComment::find("diskusi_id='$diskusi_id'")->count();
            $result[$i]['count_share'] = DiskusiShare::find("diskusi_id='$diskusi_id'")->count();
            $report = Diskusi::getReport($result[$i]['id']);

            if (empty($report)) {
                $result[$i]['laporan'] = null;
            } else {
                $result[$i]['laporan'] = $report->simpleToArray();
            }
        }

        $totalCount = $queryCount
            ->where($strWhere)
            ->bind($bind)
            ->execute()
            ->toArray();

        return new PaginatedResponse($result, $totalCount[0]["jumlah"], $params["limit"], $params["offset"]);
    }

    public function hotDiskusi()
    {
        $params = $this->request->get();

        $params["limit"] = array_key_exists("limit", $params) ? $params["limit"] : 3;
        $params["offset"] = array_key_exists("offset", $params) ? $params["offset"] : 0;
        $params["type"] = array_key_exists("type", $params) ? $params["type"] : 0;
        $params["produk_id"] = array_key_exists("produk_id", $params) ? $params["produk_id"] : 0;
        $params['produk'] = array_key_exists('produk', $params) ? $params['produk'] : 'all';

        $validatedRequest = $this->validate(
            $params,
            [
                'limit' => ['validators' => ['digit']],
                'offset' => ['validators' => ['digit']],
                'type' => ['validators' => ['digit']],
                'produk_id' => ['validators' => ['digit']],
                'produk' => ['validators' => [
                    [
                        'name' => 'inclusion_in',
                        'params' => ['all', 'diskusi', 'keluhan']
                    ]
                ]]
            ]
        );

        $viewName = ($validatedRequest["type"] == 1 ? "jaga.view_trending_berita" : "jaga.view_hot_diskusi");
        $columns = "
            id,
            user_id,
            title,
            uuid,
            jenis_kelamin,
            nama,
            checkmark_verified,
            nama_produk,
            updated_at,
            content,
            total_like,
            total_comment,
            total_share,
            is_anonymous,
            foto
        ";

        $whereArr = [];
        $where = '';
        $bind = [
            "limit" => $validatedRequest["limit"],
            "offset" => $validatedRequest["offset"]
        ];


        if ($params['produk'] != 'all') {
            if ($params['produk'] == 'diskusi') {
                $whereArr[] = "id_produk <> :id_index";
            } else if ($params['produk'] == 'keluhan') {
                $whereArr[] = "id_produk = :id_index";

                //NOTE: Harusnya tanpa Bansos & Covid, tapi berhubung tidak ada Hot Keluhan, jadi tidak apa2
            }
            $bind["id_index"] = 9;
        }

        if ($validatedRequest['produk_id'] > 0) {
            $whereArr[] = "id_produk = :id_produk";
            $bind['id_produk'] = $validatedRequest['produk_id'];
        }

        if (count($whereArr) > 0) {
            $where = 'WHERE ' . implode(' AND ', $whereArr);
        }

        $result = $this->rawQuery(
            "SELECT $columns FROM  $viewName  $where  LIMIT :limit OFFSET :offset",
            $bind
        );

        $filter = new Filter();
        $contentLength = 500;
        for ($i = 0; $i < count($result); $i++) {
            $result[$i]["profile_picture_url"] = $result[$i]["is_anonymous"] == 1 || $result[$i]["foto"] == null || $result[$i]["foto"] == '' || $result[$i]["foto"] == '-' ? null : MediaHelper::getPresignedUrlFoto($result[$i], UmUser::class, $this->config, $this->minio);
            unset($result[$i]["presigned_url"]);
            unset($result[$i]["presigned_exp"]);

            if ($result[$i]["is_anonymous"] == 1) {
                $result[$i]["nama"] = "Anonim";
            }

            $result[$i]["content"] = $filter->sanitize($result[$i]["content"], 'striptags');
            if (strlen($result[$i]["content"]) >= $contentLength) {
                $result[$i]["content"] = mb_substr($result[$i]["content"], 0, ($contentLength - 3)) . "...";
            }

            $censorResult  = $this->getCensoredContent($result[$i]["content"]);
            if ($censorResult['is_censored']) {
                $result[$i]["content"] = $censorResult['censored_content'];
            }
            $result[$i]['is_censored'] = $censorResult['is_censored'];

            $result[$i]['slug'] = CommonTools::slug($result[$i]['title']);
        }

        $finalResult = [];

        foreach ($result as $item) {
            unset($item["is_anonymous"]);
            unset($item["is_censored"]);
            $finalResult[] = $item;
        }

        return new CollectionPaginatedResponse($finalResult);
    }

    public function kontenDiskusi($uuid)
    {
        $service = new DiskusiService($this->config, $this->shared, null, $this->minio);
        $result = $service->getDiskusiDetailForKonten($uuid);

        return new PaginatedResponse($result, 1, 1, 0);
    }

    private function diskusiCountLike($diskusiId)
    {
        return DiskusiLike::find(
            [
                "columns" => "count(1) as jumlah",
                "diskusi_id = :diskusi_id:",
                "bind" => [
                    "diskusi_id" => $diskusiId
                ]
            ]
        )
            ->toArray()[0]["jumlah"];
    }

    private function diskusiCommentCountLike($commentId)
    {
        return DiskusiCommentLike::find(
            [
                "columns" => "count(1) as jumlah",
                "comment_id = :comment_id:",
                "bind" => [
                    "comment_id" => $commentId
                ]
            ]
        )
            ->toArray()[0]["jumlah"];
    }

    private function diskusiCommentCountReplies($commentId)
    {
        return (int) DiskusiComment::find(
            [
                "columns" => "count(1) as jumlah",
                "comment_id = :comment_id:",
                "bind" => [
                    "comment_id" => $commentId
                ]
            ]
        )
            ->toArray()[0]["jumlah"];
    }

    private function sendNotifMention($params, $diskusi)
    {
        if (array_key_exists('mentions', $params) && !empty($params["mentions"])) {
            if (is_string($params["mentions"])) {
                try {
                    $mentions = json_decode($params["mentions"]);
                    if (!is_array($mentions)) {
                        $mentions = explode(",", $params["mentions"]);
                    }
                } catch (\Exception $e) {
                    $mentions = [];
                }
                $mentions = array_unique($mentions);
            } elseif (is_array($params["mentions"])) {
                $mentions = array_unique($params["mentions"]);
            } else {
                $mentions = [];
            }

            foreach ($mentions as $mention) {
                $diskusiMentionUser = new DiskusiMentionUser();
                $diskusiMentionUser->user_id = $this->shared->user->um_id;
                $diskusiMentionUser->tenant_id = -1234;
                $diskusiMentionUser->user_mentioned_id = $mention;
                $diskusiMentionUser->tenant_mentioned_id = -1234;
                $diskusiMentionUser->diskusi_id = $diskusi->id;
                $diskusiMentionUser->create();

                if ($diskusi->type == 0) {
                    $type = "diskusi";
                    $typeEn = "discussion";
                } else {
                    $type = "cerita sukses";
                    $typeEn = "success story";
                }

                $bodyId = "menyebut Anda dalam " . $type . " " . $params["judul"];
                $bodyEn = "mentioned you in " . $typeEn . " " . $params["judul"];
                $this->sendNotif($mention, $this->shared->user->um_id, $bodyId, ($diskusi->type + 1), $diskusi->uuid, $bodyEn);
            }
        }
    }

    private function getClickAction($action, $uuid)
    {
        $clickAction = "";
        if ($action == NotificationHelper::ACTION_DISKUSI) {
            $clickAction = "diskusi";
        } else {
            $clickAction = "cerita";
        }

        $clickAction .= "/detail?uuid=" . $uuid;

        return $clickAction;
    }

    private function checkMimeTypes()
    {
        $errors = [];
        foreach ($this->request->getUploadedFiles() as $file) {
            if (!in_array($file->getRealType(), $this->config["allowed_mime_types"])) {
                array_push($errors, [
                    "field" => "image",
                    "filename" => $file->getName(),
                    "message" => "Tipe file (" . $file->getName() . ") tidak diizinkan:" . $file->getRealType()
                ]);
            }
        }

        if (count($errors) > 0) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Invalid Mime Types", $errors);
        }
    }

    private function processDiskusiAttachment($subFolder, $captions, $captionsEn = null)
    {
        $attachments = [];
        $random = new \Phalcon\Security\Random();

        if (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::MINIO) {
            if ($this->request->hasFiles()) {
                $uploadedFiles = $this->request->getUploadedFiles();

                /** @var MinioService $minioService */
                $minioService = $this->minio;
                foreach ($uploadedFiles as $index => $file) {
                    $uuid = $random->uuid();

                    if ($minioService->isSecondaryS3Available()) {
                        $minioService->baseUploadUsingSecondaryS3(
                            $file->getTempName(),
                            $file->getRealType(),
                            true,
                            $subFolder . DIRECTORY_SEPARATOR . $file->getName()
                        );
                    } else {
                        $minioService->base_upload_attachment(
                            $file->getTempName(),
                            $file->getRealType(),
                            true,
                            $subFolder . DIRECTORY_SEPARATOR . $file->getName()
                        );
                    }

                    $caption = is_array($captions) && array_key_exists($index, $captions) ? $captions[$index] : null;
                    $captionEn = is_array($captionsEn) && array_key_exists($index, $captionsEn) ? $captionsEn[$index] : null;

                    $diskusiAttachment = new DiskusiAttachment();
                    $diskusiAttachment->filename = $file->getName();
                    $diskusiAttachment->filetype = $file->getRealType();
                    $diskusiAttachment->uuid = $uuid;
                    $diskusiAttachment->caption = $caption;
                    $diskusiAttachment->caption_en = $captionEn;
                    $attachments[] = $diskusiAttachment;
                }
            }
        } elseif (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::LOCAL) {
            if ($this->request->hasFiles()) {
                foreach ($this->request->getUploadedFiles() as $index => $file) {
                    $subPath = getenv('PRIVATE_FOLDER') . $subFolder;

                    if (!file_exists($subPath) && !is_dir($subPath)) {
                        mkdir($subPath, 0770, true);
                    }

                    $filename = $subPath . DIRECTORY_SEPARATOR . $file->getName();
                    $file->moveTo($filename);

                    $caption = is_array($captions) && array_key_exists($index, $captions) ? $captions[$index] : null;
                    $captionEn = is_array($captionsEn) && array_key_exists($index, $captionsEn) ? $captionsEn[$index] : null;

                    $diskusiAttachment = new DiskusiAttachment();
                    $diskusiAttachment->filename = $file->getName();
                    $diskusiAttachment->filetype = mime_content_type($filename);
                    $diskusiAttachment->caption = $caption;
                    $diskusiAttachment->caption_en = $captionEn;
                    $attachments[] = $diskusiAttachment;
                }
            }
        }
        return $attachments;
    }

    private function processCommentAttachment($subFolder)
    {
        $attachments = [];
        $random = new \Phalcon\Security\Random();

        if (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::MINIO) {
            if ($this->request->hasFiles()) {
                $uploadedFiles = $this->request->getUploadedFiles();

                foreach ($uploadedFiles as $file) {
                    $uuid = $random->uuid();

                    /** @var MinioService $minioService */
                    $minioService = $this->minio;
                    if ($minioService->isSecondaryS3Available()) {
                        $minioService->baseUploadUsingSecondaryS3(
                            $file->getTempName(),
                            $file->getRealType(),
                            true,
                            $subFolder . DIRECTORY_SEPARATOR . $file->getName()
                        );
                    } else {
                        $minioService->base_upload_attachment(
                            $file->getTempName(),
                            $file->getRealType(),
                            true,
                            $subFolder . DIRECTORY_SEPARATOR . $file->getName()
                        );
                    }

                    $commentAttachment = new CommentAttachment();
                    $commentAttachment->filename = $file->getName();
                    $commentAttachment->filetype = $file->getRealType();
                    $commentAttachment->uuid = $uuid;
                    array_push($attachments, $commentAttachment);
                }
            }
        } elseif (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::LOCAL) {
            if ($this->request->hasFiles()) {
                foreach ($this->request->getUploadedFiles() as $file) {
                    $subPath = getenv('PRIVATE_FOLDER') . $subFolder;

                    if (!file_exists($subPath) && !is_dir($subPath)) {
                        mkdir($subPath, 0770, true);
                    }

                    $filename = $subPath . DIRECTORY_SEPARATOR . $file->getName();
                    $file->moveTo($filename);

                    $commentAttachment = new CommentAttachment();
                    $commentAttachment->filename = $file->getName();
                    $commentAttachment->filetype = mime_content_type($filename);
                    array_push($attachments, $commentAttachment);
                }
            }
        }
        return $attachments;
    }

    private function sendNotifEditDiskusi($diskusiId, $uuid)
    {
        $result = DiskusiMentionUser::query()
            ->columns("UmUser.um_user_name as nama, DiskusiMentionUser.user_mentioned_id, Diskusi.title, Diskusi.type")
            ->join('UmUser', 'DiskusiMentionUser.user_id = UmUser.um_id')
            ->join('Diskusi', 'DiskusiMentionUser.diskusi_id = Diskusi.id')
            ->where("DiskusiMentionUser.diskusi_id = :diskusi_id:")
            ->bind(
                [
                    "diskusi_id" => $diskusiId
                ]
            )
            ->execute()
            ->toArray();

        foreach ($result as $res) {
            $bodyId = "mengubah konten diskusi " . $res["title"];
            $bodyEn = "changed discussion content " . $res["title"];
            $this->sendNotif($res["user_mentioned_id"], $this->shared->user->um_id, $bodyId, ($res["type"] + 1), $uuid, $bodyEn);
        }
    }

    private function processRemoveAttachment($subFolder, $params)
    {
        if (array_key_exists('deleted_attachments', $params)) {
            if (strlen($params["deleted_attachments"]) > 0) {
                $daArr = explode(",", $params["deleted_attachments"]);
                foreach ($daArr as $da) {
                    $result = DiskusiAttachment::find(
                        [
                            "uuid = :uuid:",
                            "bind" => [
                                "uuid" => $da
                            ]
                        ]
                    );

                    if ($result->count() == 1) {
                        $att = $result[0];

                        if (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::LOCAL) {
                            $filename = getenv('PRIVATE_FOLDER') . $subFolder . DIRECTORY_SEPARATOR . $att->filename;

                            if (file_exists($filename)) {
                                unlink($filename);
                            }
                        } elseif (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::MINIO) {
                            /** @var MinioService $minioService */
                            $minioService = $this->minio;
                            if ($minioService->isSecondaryS3Available()) {
                                $minioService->deleteUsingSecondaryS3($att->filename, true, $subFolder);
                            } else {
                                $minioService->delete($att->filename, true, $subFolder);
                            }
                        }

                        $result->delete();
                    }
                }
            }
        }
    }

    private function processEditCaption($diskusi, $captions, $captionsEn)
    {
        $attachments = DiskusiAttachment::find([
            'conditions' => 'diskusi_id = :id_diskusi:',
            'bind' => ['id_diskusi' => $diskusi->id]
        ]);

        foreach ($attachments as $attachment) {
            if (is_array($captions) && array_key_exists($attachment->uuid, $captions)) {
                $attachment->caption = $captions[$attachment->uuid];
                if (is_array($captionsEn) && array_key_exists($attachment->uuid, $captionsEn)) {
                    $attachment->caption_en = $captionsEn[$attachment->uuid];
                }
                $attachment->save();
            }
        }
    }

    public function editDiskusi($uuid)
    {
        date_default_timezone_set('Asia/Jakarta');
        $this->checkScopes(['discussion.create']);

        $params = $this->request->getPost();

        $params["link_terkait"] = array_key_exists("link_terkait", $params) ? $params["link_terkait"] : '';
        $validatedRequest = $this->validate(
            $params,
            [
                'judul' => ['validators' => ['required']],
                'is_anonymous' => ['validators' => ['required', 'digit']],
                'konten_diskusi' => ['validators' => ['required']],
                'link_terkait' => ['validators' => []],
                'mentions' => ['validators' => []],
                'deleted_attachments' => ['validators' => []],
            ]
        );

        $service = new DiskusiService($this->config, $this->shared, $this->request, $this->minio);
        $result = $service->editDiskusi($uuid, $validatedRequest);

        return new ObjectResponse(
            [
                "success" => true,
                "data" => [
                    "diskusi_uuid" => $result['diskusi']->uuid,
                    "diskusi_id" => $result['diskusi']->id
                ]
            ]
        );
    }

    private function createNewTag($label)
    {
        $tag = new Tags();
        $tag->label = $label;
        $tag->create();
        return $tag->id;
    }

    public function editCerita($uuid)
    {
        date_default_timezone_set('Asia/Jakarta');
        $this->checkScopes(['admin.view']);

        $params = $this->request->getPost();
        $validatedRequest = $this->validate(
            $params,
            [
                'judul' => ['validators' => ['required']],
                'judul_en' => ['validators' => []],
                'is_anonymous' => ['validators' => ['required']],
                'konten_diskusi' => ['validators' => ['required']],
                'konten_diskusi_en' => ['validators' => []],
                'release_date' => ['validators' => ['datetime']],
                'tags' => ['validators' => []],
                'captions' => ['validators' => []],
                'captions_en' => ['validators' => []],
                'link_terkait' => ['validators' => []],
            ]
        );

        $params["link_terkait"] = array_key_exists("link_terkait", $params) ? $params["link_terkait"] : '';

        $result = Diskusi::find(
            [
                "uuid = :uuid:",
                "bind" => [
                    "uuid" => $uuid
                ]
            ]
        );

        if ($result->count() != 1) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, 'Diskusi tidak ditemukan');
        }

        $diskusi = $result[0];

        if ($diskusi->isDeleted()) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, 'Diskusi telah dikunci');
        }

        if (!empty($diskusi->report())) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Diskusi telah dilaporkan');
        }

        $this->checkMimeTypes();

        $now = date('Y-m-d H:i:s');
        $diskusi->is_anonymous = $params["is_anonymous"] ? 1 : 0;
        $diskusi->title = $params["judul"];
        $diskusi->title_en = $params["judul_en"];
        $diskusi->release_date = $params["release_date"];
        $diskusi->content = $params["konten_diskusi"];
        $diskusi->content_en = $params["konten_diskusi_en"];
        $diskusi->link_terkait = $params["link_terkait"];
        $diskusi->updated_at = $now;

        $this->sendNotifEditDiskusi($diskusi->id, $uuid);

        $this->sendNotifMention($params, $diskusi);

        $subFolder =
            'diskusi_attachment' .
            DIRECTORY_SEPARATOR .
            $this->shared->user->um_id .
            DIRECTORY_SEPARATOR .
            $diskusi->id;

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

            $this->processRemoveAttachment($subFolder, $params);
            $this->processEditCaption($diskusi, $validatedRequest['captions'], $validatedRequest['captions_en']);

            if (!empty($validatedRequest['captions_en'])) {
                $diskusi->diskusiAttachment = $this->processDiskusiAttachment($subFolder, $validatedRequest['captions'], $validatedRequest['captions_en']);
            } else {
                $diskusi->diskusiAttachment = $this->processDiskusiAttachment($subFolder, $validatedRequest['captions']);
            }

            $diskusi->save();

            if (!empty($validatedRequest['tags']) && $validatedRequest['tags'] != null) {
                $savedBeritaTagsIds = [];
                $tags = json_decode($validatedRequest['tags'], true);
                foreach ($tags as $tag) {
                    $this->validate(
                        $tag,
                        [
                            'label' => [
                                'validators' => [
                                    [
                                        'name' => 'max_string',
                                        'params' => 30
                                    ]
                                ]
                            ]
                        ]
                    );
                    if (!array_key_exists('id', $tag)) {
                        $tag['id'] = $this->createNewTag($tag['label']);
                    } else if (empty($tag['id']) || $tag['id'] == null || $tag['id'] < 1) {
                        $tag['id'] = $this->createNewTag($tag['label']);
                    }
                    $beritaTags = BeritaTags::findFirst([
                        'id_diskusi = :id_diskusi: AND id_tags = :id_tags:',
                        'bind' => [
                            'id_diskusi' => $diskusi->id,
                            'id_tags' => $tag['id']
                        ]
                    ]);
                    if (empty($beritaTags) || $beritaTags == null) {
                        $beritaTags = new BeritaTags();
                        $beritaTags->id_diskusi = $diskusi->id;
                        $beritaTags->id_tags = $tag['id'];
                        $beritaTags->create();
                    }
                    $savedBeritaTagsIds[] = (int)$beritaTags->id;
                }
                if (!empty($savedBeritaTagsIds)) {
                    $deleteBeritaTags = BeritaTags::find([
                        "conditions" => "id NOT IN ({ids:array}) AND id_diskusi = :id_diskusi:",
                        'bind' => [
                            "ids" => array_values($savedBeritaTagsIds),
                            'id_diskusi' => $diskusi->id
                        ]
                    ]);
                } else {
                    $deleteBeritaTags = BeritaTags::find([
                        "conditions" => "id_diskusi = :id_diskusi:",
                        'bind' => [
                            'id_diskusi' => $diskusi->id
                        ]
                    ]);
                }
                foreach ($deleteBeritaTags as $item) {
                    $item->delete();
                }
            }

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

        //region Update Meilisearch Jika Type = 1 atau Berita
        if ($diskusi->type == 1 && filter_var(getenv("MEILI_SEARCH_ACTIVE"), FILTER_VALIDATE_BOOLEAN)) {
            try {
                $dataBerita = Diskusi::query()
                    ->columns("Diskusi.*, KategoriDiskusi.title as title_kategori_diskusi, string_agg(Tags.label, ', ') AS tags")
                    ->leftJoin("KategoriDiskusi", "Diskusi.kategori_diskusi_id = KategoriDiskusi.id")
                    ->leftJoin("BeritaTags", "BeritaTags.id_diskusi = Diskusi.id")
                    ->leftJoin("Tags", "BeritaTags.id_tags = Tags.id")
                    ->where("
                            Diskusi.id = :id:
                            AND Diskusi.type = :type:
                            AND Diskusi.is_deleted = :is_deleted:
                        ")
                    ->bind([
                        "id" => $diskusi->id,
                        "type" => 1,
                        "is_deleted" => 0
                    ])
                    ->groupBy("Diskusi.id, KategoriDiskusi.title")
                    ->orderBy("Diskusi.id ASC")
                    ->execute()->toArray();

                if (count($dataBerita) > 0) {
                    $itemBerita = $dataBerita[0];

                    $content = strip_tags($itemBerita['diskusi']->content);
                    if (strlen($content) > 255) $content = mb_substr($content, 0, 255) . "...";

                    $meilisearch = new MeiliSearchService($this);
                    $itemMeili = [
                        "id" => "berita-" . $itemBerita['diskusi']->id,
                        "sector" => $meilisearch::SECTOR_BERITA_JAGA,
                        "category" => $meilisearch::CATEGORY_BERITA,
                        "substance" => $meilisearch::SUBSTANCE_DETAIL_BERITA,
                        "title" => "[" . $itemBerita['title_kategori_diskusi'] . "] " . $itemBerita['diskusi']->title,
                        "content" => $content,
                        "keywords" => 'kabar berita tentang mengenai ' . strip_tags($itemBerita['diskusi']->content) . ". Tags: " . $itemBerita['tags'],
                        "FEURL" => "/berita/" . $itemBerita['diskusi']->uuid
                    ];
                    $meilisearch->updateSingleDocument($itemMeili);
                }
            } catch (Exception $exception) {
                LogHelper::apiLogError("Edit Berita-Meilisearch", "", $exception->getMessage());
                //Ignore the catch
            }
        }
        //endregion

        //region update sitemap.xml
        if ($diskusi->type == 1) {
            DiskusiService::generateSitemap($this->logger, $this->config['application']['publicDir']);
        }
        //endregion

        return new ObjectResponse(
            [
                "success" => true,
                "data" => [
                    "diskusi_uuid" => $diskusi->uuid,
                    "diskusi_id" => $diskusi->id
                ]
            ]
        );
    }

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

        $result = Diskusi::find(
            [
                "uuid = :uuid:",
                "bind" => [
                    "uuid" => $uuid,
                ]
            ]
        );

        if ($result->count() == 1) {
            $diskusi = $result[0];
            $diskusi->is_deleted = 1;
            $diskusi->save();

            //region Delete Berita dari Meilisearch
            if ($diskusi->type == 1 && filter_var(getenv("MEILI_SEARCH_ACTIVE"), FILTER_VALIDATE_BOOLEAN)) {
                try {
                    $meilisearch = new MeiliSearchService($this);
                    $meilisearch->deleteDocument("berita-" . $diskusi->id);
                } catch (Exception $exception) {
                    //Don't Throw
                    LogHelper::apiLogError("Hapus Berita-Meilisearch", "", $exception->getMessage());
                }
            }
            //endregion

            //region update sitemap.xml
            if ($diskusi->type == 1) {
                DiskusiService::generateSitemap($this->logger, $this->config['application']['publicDir']);
            }
            //endregion

            return new ObjectResponse(
                [
                    "success" => true,
                    "data" => [
                        "diskusi_id" => $diskusi->uuid
                    ]
                ]
            );
        } else {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, 'Cerita tidak ditemukan');
        }
    }

    public function hapusDiskusi($uuid)
    {
        date_default_timezone_set('Asia/Jakarta');
        $this->checkScopes(['discussion.create']);

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

        $result = Diskusi::find(
            [
                "uuid = :uuid: and user_id = :user_id:",
                "bind" => [
                    "uuid" => $uuid,
                    "user_id" => $this->shared->user->um_id
                ]
            ]
        );

        if ($result->count() == 1) {
            $diskusi = $result[0];
            $diskusi->is_deleted = 1;
            $diskusiBansos = DiskusiBansos::findFirst(
                [
                    "id_diskusi = :id_diskusi: and deleted_at is null",
                    "bind" => [
                        "id_diskusi" => $diskusi->id
                    ]
                ]
            );

            if (!empty($diskusiBansos)) {
                if ($diskusiBansos->status != DiskusiBansos::NEW) {
                    throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Tidak dapat menghapus keluhan yang sudah diproses');
                }

                $diskusiBansos->deleted_at = $now;
                $diskusiBansos->save();

                $diskusiBansosNIK = DiskusiBansosNIK::find(
                    [
                        "id_diskusi_bansos = :id_diskusi_bansos: and deleted_at is null",
                        "bind" => [
                            "id_diskusi_bansos" => $diskusiBansos->id,
                        ],
                    ]
                );

                if ($diskusiBansosNIK->count() > 0) {
                    foreach ($diskusiBansosNIK as $item) {
                        $item->deleted_at = $now;
                        $item->save();
                    }
                }
            }
            $diskusi->save();
            return new ObjectResponse(
                [
                    "success" => true,
                    "data" => [
                        "diskusi_id" => $diskusi->uuid
                    ]
                ]
            );
        } else {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, 'Diskusi tidak ditemukan');
        }
    }

    public function detilDiskusiStandard($uuid)
    {
        return $this->detilDiskusi(false, $uuid);
    }

    public function detilDiskusiFull($uuid)
    {
        return $this->detilDiskusi(true, $uuid);
    }

    public function detilDiskusiBansos($uuid)
    {
        $diskusi = Diskusi::findFirst([
            'conditions' => "Diskusi.uuid = :uuid:",
            'bind' => [
                "uuid" => $uuid
            ]
        ]);

        if ($diskusi == null || empty($diskusi))
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Diskusi tidak ditemukan!");

        if (array_key_exists('user', $this->shared)) {
            if ($this->shared->user->um_id != $diskusi->user_id) {
                try {
                    $this->checkOneOfScopes(['admin.view', 'bansos.view_all', 'bansos.view_assigned', 'bansos.confirm_all', 'bansos.confirm_assigned',  'keluhan.ikn.follow_up', 'keluhan.desa.follow_up', 'keluhan.pendidikan.follow_up']);
                    //Scope yg dihapus: 'bansos.follow_up'
                    //dihapus karena saat anonim tidak boleh melihat detail nama

                    return $this->detilDiskusi(true, $uuid);
                } catch (Exception $e) {
                    return $this->detilDiskusi(false, $uuid);
                }
            }
        } else {
            return $this->detilDiskusi(false, $uuid);
        }

        return $this->detilDiskusi(true, $uuid);
    }

    public function detilDiskusi($isFull, $uuid)
    {
        if ($uuid == 'undefined')
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Parameter UUID tidak boleh "undefined"!');

        $diskusi = Diskusi::findFirst([
            'conditions' => "Diskusi.uuid = :uuid:",
            'bind' => [
                "uuid" => $uuid
            ]
        ]);

        if (empty($diskusi)) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, 'Diskusi tidak ditemukan');
        } else {
            if ($isFull) {
                $diskusiBansos = DiskusiBansos::findByIdDiskusi($diskusi->id);
                if ($this->shared->user->um_id != $diskusi->user_id) {
                    if (empty($diskusiBansos)) {
                        $this->checkScopes(['admin.view']);
                    } else {
                        $this->checkOneOfScopes(['admin.view', 'bansos.view_all', 'bansos.view_assigned', 'bansos.confirm_all', 'bansos.confirm_assigned', 'bansos.follow_up', 'keluhan.ikn.follow_up', 'keluhan.desa.follow_up', 'keluhan.pendidikan.follow_up']);
                    }
                }
            }

            $diskusi->views += 1;
            $diskusi->save();

            $data = $isFull ?
                $diskusi->toJsonForDetailFull(null, $this->config["profile_picture_base_url"], $this->config, $this->minio) :
                $diskusi->toJsonForDetail(null, $this->config["profile_picture_base_url"], $this->config, $this->minio);

            $data['tags'] = $diskusi->tags->toArray();

            //Rekam UserActivity
            if ($this->shared) {
                $service = new ActivityService($this->shared, $this->logger);
                $service->createActivity($data['produk_id'], $data['id_objek'], $diskusi->id);
            }
            if (isset($this->shared->user->um_id)) {
                $data["liked"] = DiskusiLike::count(
                    [
                        "diskusi_id = :diskusi_id: and user_id = :user_id:",
                        "bind" => [
                            "diskusi_id" => $diskusi->id,
                            "user_id" => $this->shared->user->um_id
                        ]
                    ]
                ) > 0;
            } else {
                $data["liked"] = false;
            }

            if (!$isFull) {
                $censorResult = $this->getCensoredContent($data["content"]);
                if ($censorResult['is_censored']) {
                    $data["content"] = $censorResult['censored_content'];
                }
                $data['is_censored'] = $censorResult['is_censored'];
            }

            $data['slug'] = CommonTools::slug($data['title']);

            if (array_key_exists('id_topik', $data) && !empty($data['id_topik'])) {
                $topik = TopikDiskusiBansos::findFirst($data['id_topik']);

                if (empty($topik->deleted_at)) {
                    $data['topik_type'] = $topik->type;
                }

                //Region get Pemilu Detail
                if ($topik->type == TopikDiskusiBansos::TYPE_PEMILU) {
                    $keluhanPemilu = DiskusiKeluhanPemilu::findFirst([
                        'id_diskusi = ?0',
                        'bind' => [
                            $data['id']
                        ]
                    ]);

                    $objekPemilu = $keluhanPemilu->objekKeluhanPemilu;

                    $keluhanPemilu = $keluhanPemilu->toArray();
                    $keluhanPemilu['objek_pemilu'] = $objekPemilu;
                    $explJenisGratifikasi = explode(';', $keluhanPemilu['jenis_gratifikasi']);
                    if (count($explJenisGratifikasi) > 0 && !empty($keluhanPemilu['jenis_gratifikasi'])) {
                        $nameJenisGratifikasi = [];
                        foreach ($explJenisGratifikasi as $item) {
                            $nameJenisGratifikasi[] = DiskusiKeluhanPemilu::MAPPING_JENIS_GRATIFIKASI[$item];
                        }
                        $keluhanPemilu['jenis_gratifikasi_name'] = implode(';', $nameJenisGratifikasi);
                    } else {
                        $keluhanPemilu['jenis_gratifikasi_name'] = "";
                    }

                    $data['detail_pemilu'] = $keluhanPemilu;
                    $data['nama_objek'] = str_replace("Pemerintah ", "", $data['nama_objek']);
                }
                //endregion
            }

            return new PaginatedResponse([$data], 1, 1, 0);
        }
    }

    public function diskusiByUser()
    {
        $this->checkIsAuthenticated();

        $params = $this->request->get();
        $params['limit'] = $this->getDefaultLimit($params);
        $params["offset"] = array_key_exists("offset", $params) ? $params["offset"] : 0;
        $params["produk_id"] = array_key_exists("produk_id", $params) ? $params["produk_id"] : null;

        $validatedRequest = $this->validate(
            $params,
            [
                'user_id' => ['validators' => ['required', 'digit']],
                'limit' => ['validators' => ['digit']],
                'offset' => ['validators' => ['digit']],
                'produk_id' => ['validators' => ['digit']]
            ]
        );

        $strWhere = 'Diskusi.is_deleted = 0 AND Diskusi.is_anonymous = 0 AND Diskusi.user_id = :user_id:';
        $bind['user_id'] = $validatedRequest['user_id'];

        if (!is_null($validatedRequest['produk_id'])) {
            $strWhere .= " and KategoriDiskusi.produk_id = :produk_id:";
            $bind["produk_id"] = $validatedRequest["produk_id"];
        }

        $totalResult = Diskusi::query()
            ->columns("count(1) as counter")
            ->join('KategoriDiskusi', 'Diskusi.kategori_diskusi_id = KategoriDiskusi.id')
            ->leftjoin('Produk', 'KategoriDiskusi.produk_id = Produk.id_index')
            ->where($strWhere)
            ->bind($bind)
            ->execute()[0]['counter'];

        $orderClause = 'Diskusi.created_at';

        $result = Diskusi::query()
            ->columns("Diskusi.id as diskusi_id, Diskusi.uuid as diskusi_uuid, Diskusi.title as judul_diskusi, Diskusi.content as isi_diskusi, Diskusi.user_id as user_diskusi, Diskusi.views as views, COALESCE(Produk.nama, '-') as nama_produk, Diskusi.created_at as created_at")
            ->join('KategoriDiskusi', 'Diskusi.kategori_diskusi_id = KategoriDiskusi.id')
            ->leftjoin('Produk', 'KategoriDiskusi.produk_id = Produk.id_index')
            ->where($strWhere)
            ->bind($bind)
            ->limit($validatedRequest["limit"], $validatedRequest["offset"])
            ->orderBy($orderClause . ' DESC')
            ->execute()->toArray();

        return new PaginatedResponse($result, $totalResult, $params["limit"], $params["offset"]);
    }

    public function kirimKomentar()
    {
        date_default_timezone_set('Asia/Jakarta');
        $this->checkScopes(['discussion.react']);

        $rawBody = $this->request->getJsonRawBody(true);
        $rawBody["komentar_id"] = array_key_exists('komentar_id', $rawBody) ? $rawBody["komentar_id"] : 0;

        $validatedRequest = $this->validate(
            $rawBody,
            [
                'diskusi_id' => ['validators' => ['required', 'digit']],
                'komentar' => ['validators' => ['required']],
                'komentar_id' => ['validators' => ['digit']],
                'confirm_all_status' => ['validators' => []],
                'confirm_assigned_status' => ['validators' => []],
                'follow_up_status' => ['validators' => []],
            ]
        );

        $level = 0;
        try {
            $commentOwnerId = 0;

            $diskusi = Diskusi::findFirst(
                [
                    "id = :id:",
                    "bind" => [
                        "id" => $rawBody["diskusi_id"]
                    ]
                ]
            );

            if (!$diskusi) {
                throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, 'Komentar gagal disimpan. Diskusi tidak ditemukan');
            }

            if ($diskusi->isDeleted()) {
                throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, 'Komentar gagal disimpan. Diskusi telah dikunci');
            }

            if (!empty($diskusi->report())) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Komentar gagal disimpan. Diskusi telah dilaporkan');
            }

            if ($diskusi->type == 0) {
                $type = "diskusi";
                $typeEn = "discussion";
            } else {
                $type = "cerita sukses";
                $typeEn = "success story";
            }

            $diskusiBansos = DiskusiBansos::findFirst(
                [
                    "deleted_at is null and id_diskusi = :id_diskusi:",
                    "bind" => [
                        "id_diskusi" => $diskusi->id
                    ]
                ]
            );

            $topik_type = "";
            $topik_subtype = "";
            if (!empty($diskusiBansos)) {
                $topik = TopikDiskusiBansos::findFirst([
                    'conditions' => 'deleted_at IS NULL and id = ?0',
                    'bind' => [
                        $diskusiBansos->id_topik
                    ]
                ]);

                $topik_type = empty($topik) ? TopikDiskusiBansos::TYPE_BANSOS : $topik->type;
                $topik_subtype = empty($topik) ? null : $topik->subtype;
                if ((!array_key_exists('follow_up_status', $validatedRequest) || empty($validatedRequest['follow_up_status']) || $validatedRequest['follow_up_status'] == 'null') &&
                    (($this->hasScopes(['bansos.follow_up']) && $topik_type == TopikDiskusiBansos::TYPE_BANSOS) ||
                        ($this->hasScopes(['covid.dinkes_pemda']) && $topik_type == TopikDiskusiBansos::TYPE_COVID) ||
                        ($this->hasScopes(['keluhan.ikn.follow_up']) && $topik_type == TopikDiskusiBansos::TYPE_IKN) ||
                        ($this->hasScopes(['keluhan.desa.follow_up']) && $topik_type == TopikDiskusiBansos::TYPE_DESA) ||
                        ($this->hasScopes(['keluhan.kampus.follow_up']) && $topik_type == TopikDiskusiBansos::TYPE_KAMPUS) ||
                        ($this->hasScopes(['keluhan.pelabuhan.follow_up']) && $topik_type == TopikDiskusiBansos::TYPE_PELABUHAN) ||
                        ($this->hasScopes(['keluhan.bpjs.follow_up']) && $topik_type == TopikDiskusiBansos::TYPE_BPJS_KESEHATAN) ||
                        ($this->hasScopes(['keluhan.pendidikan.follow_up']) && $topik_type == TopikDiskusiBansos::TYPE_PENDIDIKAN)
                    )
                    && $diskusiBansos->status == DiskusiBansos::CONFIRMED
                ) {
                    throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Komentar gagal disimpan. Silahkan ubah status keluhan terlebih dahulu.');
                }
            }
            if ($rawBody["komentar_id"] != 0) {
                $komentarRes = DiskusiComment::find($rawBody["komentar_id"]);

                if (count($komentarRes) < 1) {
                    throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Komentar tidak ditemukan');
                }

                if ($komentarRes[0]->isDeleted()) {
                    throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Komentar telah dihapus');
                }

                if (!empty($komentarRes[0]->report())) {
                    throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Komentar telah dilaporkan');
                }

                if ($komentarRes->count() == 1) {
                    $sourceUser = $komentarRes[0]->umUser;
                    $level = $komentarRes[0]->level;
                    $level++;
                    $commentOwnerId = $komentarRes[0]->user_id;
                    if ($commentOwnerId != $this->shared->user->um_id && $commentOwnerId != $diskusi->user_id) {
                        $bodyId = "membalas komentar Anda dalam " . $type . " " . $diskusi->title;
                        $bodyEn = "replied your comment in " . $typeEn . " " . $diskusi->title;
                        $this->sendNotif($commentOwnerId, $this->shared->user->um_id, $bodyId, ($diskusi->type + 1), $diskusi->uuid, $bodyEn);
                    }
                }
            }

            if (array_key_exists('mentions', $rawBody) && !empty($rawBody["mentions"])) {
                $mentionWhere = "";
                $mentionI = 0;

                if (is_string($rawBody["mentions"])) {
                    try {
                        $mentions = json_decode($rawBody["mentions"]);
                        if (!is_array($mentions)) {
                            $mentions = explode(",", $mentions);
                        }
                    } catch (\Exception $e) {
                        $mentions = [];
                    }
                    $mentions = array_unique($mentions);
                } elseif (is_array($rawBody["mentions"])) {
                    $mentions = array_unique($rawBody["mentions"]);
                } else {
                    $mentions = [];
                }

                foreach ($mentions as $mention) {
                    if (strlen($mentionWhere) > 0) {
                        $mentionWhere .= ", ";
                    }
                    $mentionWhere .= ":mentions" . $mentionI . ":";
                    $mentionI++;
                    $resUser = UsersRoles::find(
                        [
                            "column" => "role_id",
                            "user_id = :user_id: and (role_id = 2 or role_id = 3)",
                            "bind" => [
                                "user_id" => $mention
                            ]
                        ]
                    );
                    if ($resUser->count() > 0) {
                        $diskusiMentionUser = new DiskusiMentionUser();
                        $diskusiMentionUser->setTransaction($this->transaction);
                        $diskusiMentionUser->user_id = $this->shared->user->um_id;
                        $diskusiMentionUser->tenant_id = -1234;
                        $diskusiMentionUser->user_mentioned_id = $mention;
                        $diskusiMentionUser->tenant_mentioned_id = -1234;
                        $diskusiMentionUser->diskusi_id = $diskusi->id;
                        $res = $diskusiMentionUser->save();

                        if (!$res) {
                            $this->transaction->rollback('Error while saving mentions');
                        }
                    }

                    if ($mention != $commentOwnerId && $mention != $diskusi->user_id) {
                        $bodyId = "menyebut Anda di komentar dalam " . $type . " " . $diskusi->title;
                        $bodyEn = "mentioned you in a comment in " . $typeEn . " " . $diskusi->title;
                        $this->sendNotif($mention, $this->shared->user->um_id, $bodyId, ($diskusi->type + 1), $diskusi->uuid, $bodyEn);
                    }
                }
            }
            $now = date('Y-m-d H:i:s');
            $komentar = new DiskusiComment();
            $komentar->setTransaction($this->transaction);

            $komentar->user_id = $this->shared->user->um_id;
            $komentar->diskusi_id = $rawBody["diskusi_id"];
            $komentar->level = $level;
            $komentar->comment = $rawBody["komentar"];
            $komentar->created_at = $now;
            $komentar->updated_at = $now;
            $komentar->comment_id = $rawBody["komentar_id"];
            $getDiskusiRespon = DiskusiRespon::findFirstByIdDiskusi($rawBody['diskusi_id']);
            if (empty($getDiskusiRespon->id_komentar_pertama)) {
                $komentarId = null;
            } else {
                $komentarId = -1;
            }

            $res = $komentar->save();
            $this->transaction->commit();

            $saveRespon = false;

            if (empty($komentarId)) {
                if (empty($getDiskusiRespon)) {
                    $getDiskusi = Diskusi::findFirst($rawBody['diskusi_id']);
                    $diskusi_date = new DateTime($getDiskusi->created_at);
                    $komentar_date = new DateTime($komentar->created_at);
                    $selisih = $diskusi_date->diff($komentar_date);
                    $interval = $selisih->y . ' tahun, ' . $selisih->m . ' bulan, ' . $selisih->d . ' hari, ' . $selisih->h . ' jam, ' . $selisih->i . ' menit, ' . $selisih->s . ' detik';

                    $setDiskusiRespon = new DiskusiRespon();
                    $setDiskusiRespon->title = $getDiskusi->title;
                    $setDiskusiRespon->id_diskusi = $getDiskusi->id;
                    $setDiskusiRespon->id_komentar_pertama = $komentar->id;
                    $setDiskusiRespon->id_sektor = $getDiskusi->kategori_diskusi_id;
                    $setDiskusiRespon->created_diskusi = $getDiskusi->created_at;
                    $setDiskusiRespon->created_komentar = $komentar->created_at;
                    $setDiskusiRespon->selisih = $interval;
                    $saveRespon = $setDiskusiRespon->save();
                } else {
                    $diskusi_date = new DateTime($getDiskusiRespon->created_diskusi);
                    $komentar_date = new DateTime($komentar->created_at);
                    $selisih = $diskusi_date->diff($komentar_date);
                    $interval = $selisih->y . ' tahun, ' . $selisih->m . ' bulan, ' . $selisih->d . ' hari, ' . $selisih->h . ' jam, ' . $selisih->i . ' menit, ' . $selisih->s . ' detik';

                    $getDiskusiRespon->id_komentar_pertama = $komentar->id;
                    $getDiskusiRespon->created_komentar = $komentar->created_at;
                    $getDiskusiRespon->selisih = $interval;
                    $saveRespon = $getDiskusiRespon->save();
                }
            }

            $getDiskusi = Diskusi::findByKategoriDiskusiId($diskusi->kategori_diskusi_id);
            $getJumlahDiskusi = DiskusiLikeComment::findFirstByIdSektor($diskusi->kategori_diskusi_id);
            if (empty($getJumlahDiskusi)) {
                $jmlhDiskusi = new DiskusiLikeComment();
                $jmlhDiskusi->id_sektor = $diskusi->kategori_diskusi_id;
                $jmlhDiskusi->jumlah_diskusi = count($getDiskusi);
                $diskusiLike = DiskusiLike::findByDiskusiId($rawBody["diskusi_id"]);
                $jmlhDiskusi->jumlah_like = count($diskusiLike);
                $result = 0;
                foreach ($getDiskusi as $gd) {
                    $diskusiComment = DiskusiComment::findByDiskusiId($gd->id);
                    $resultComment = count($diskusiComment);
                    $result += $resultComment;
                }
                $jmlhDiskusi->jumlah_komentar = $result;
                $saveKomentar = $jmlhDiskusi->save();
            } else {
                $result = 0;
                foreach ($getDiskusi as $gd) {
                    $diskusiComment = DiskusiComment::findByDiskusiId($gd->id);
                    $resultComment = count($diskusiComment);
                    $result += $resultComment;
                }
                $getJumlahDiskusi->jumlah_komentar = $result;
                $saveKomentar = $getJumlahDiskusi->save();
            }

            if (!$res && !$saveRespon && !$saveKomentar) {
                $this->transaction->rollback('Error while saving komentar');
            }

            $diskusiBansos = DiskusiBansos::findFirst(
                [
                    "deleted_at is null and id_diskusi = :id_diskusi:",
                    "bind" => [
                        "id_diskusi" => $diskusi->id
                    ]
                ]
            );

            if ($diskusi->user_id != $this->shared->user->um_id) {
                if ((empty($diskusiBansos) || $diskusi->type <> 0)) {
                    $bodyId = "memberikan komentar dalam diskusi " . $diskusi->title;
                    $bodyEn = "left comment in discussion " . $diskusi->title;
                    $this->sendNotif($diskusi->user_id, $this->shared->user->um_id, $bodyId, ($diskusi->type + 1), $diskusi->uuid, $bodyEn);
                } elseif ($diskusi->type == 0) {
                    if (!empty($diskusiBansos) && $this->hasOneOfRoles(['admin_bansos_klop', 'superadmin_bansos', 'admin_bansos_korwil', 'admin_keluhan_ikn_klop', 'admin_keluhan_desa_klop', 'admin_keluhan_kampus_klop', 'admin_keluhan_pelabuhan_klop', 'admin_keluhan_bpjs_klop'])) {
                        $diskusiBansos->isresponded = 0;
                        $diskusiBansos->save();
                    }
                    $service = new DiskusiService($this->config, $this->shared, $this->request, $this->minio);
                    $service->notifyKorwilPemdaUser($diskusiBansos, DiskusiService::BANSOS_NOTIFICATION_COMMENT);
                }
            } else {
                if (!empty($diskusiBansos) && $diskusi->type == 0) {
                    if ($diskusiBansos->status == 1) {
                        $diskusiBansos->isresponded = 1;
                        $diskusiBansos->save();
                    }
                    $service = new DiskusiService($this->config, $this->shared, $this->request);
                    $service->notifyKorwilPemdaUser($diskusiBansos, DiskusiService::BANSOS_NOTIFICATION_COMMENT);
                }
            }

            $this->shared->user->jmlh_dukung_crt_mskn++;
            $this->shared->user->save();

            $bansosService = new BansosService($this);
            $bansosService->setMailHelper(new MailHelper($this->config["mail_config"]));
            $bansosService->handleActivity($validatedRequest, $komentar);
            //NOTE: Disini penerusan keluhannya

            //region Handle flagging keluhan pemilu jika yg komen adalah Bawaslu/DKPP
            if ($this->hasScopes(['keluhan.pemilu.monitoring']) && $topik_type == TopikDiskusiBansos::TYPE_PEMILU) {
                if ($diskusiBansos->id_instansi == $this->shared->user->id_instansi) {
                    if ($diskusiBansos->status != DiskusiBansos::DONE) {
                        $keluhanPemilu = DiskusiKeluhanPemilu::findFirst([
                            'id_diskusi = ?0 AND id_diskusi_bansos = ?1',
                            'bind' => [
                                $diskusiBansos->id_diskusi,
                                $diskusiBansos->id,
                            ]
                        ]);
                        $keluhanPemilu->isresponded = 1;
                        $keluhanPemilu->updated_at = $now;
                        $keluhanPemilu->respond_at = $now;
                        $keluhanPemilu->save();

                        $diskusiBansos->status = DiskusiBansos::DONE;
                        $diskusiBansos->save();

                        $bansosHistory = new DiskusiBansosHistory();
                        $bansosHistory->id_diskusi_bansos = $diskusiBansos->id;
                        $bansosHistory->keterangan = '-';
                        $bansosHistory->id_komentar = $komentar->id;
                        $bansosHistory->id_instansi = $diskusiBansos->id_instansi;
                        $bansosHistory->id_user = $this->shared->user->um_id;
                        $bansosHistory->status = DiskusiBansos::DONE;
                        $resultBansosHistory = $bansosHistory->create();
                    }
                }
            }
            //endregion

            //region Handle flagging keluhan pemilu dan subtype = SALAHGUNA jika yg komen adalah Admin Keluhan, maka langsung Done
            if ($this->hasScopes(['bansos.confirm_all']) && $topik_type == TopikDiskusiBansos::TYPE_PEMILU && $topik_subtype == TopikDiskusiBansos::SUB_TYPE_PENYALAHGUNAAN_ANGGARAN) {
                if ($diskusiBansos->status != DiskusiBansos::DONE) {
                    $keluhanPemilu = DiskusiKeluhanPemilu::findFirst([
                        'id_diskusi = ?0 AND id_diskusi_bansos = ?1',
                        'bind' => [
                            $diskusiBansos->id_diskusi,
                            $diskusiBansos->id,
                        ]
                    ]);
                    $keluhanPemilu->isresponded = 1;
                    $keluhanPemilu->updated_at = $now;
                    $keluhanPemilu->respond_at = $now;
                    $keluhanPemilu->save();

                    $diskusiBansos->status = DiskusiBansos::DONE;
                    $diskusiBansos->save();

                    $bansosHistory = new DiskusiBansosHistory();
                    $bansosHistory->id_diskusi_bansos = $diskusiBansos->id;
                    $bansosHistory->keterangan = '-';
                    $bansosHistory->id_komentar = $komentar->id;
                    $bansosHistory->id_instansi = $diskusiBansos->id_instansi;
                    $bansosHistory->id_user = $this->shared->user->um_id;
                    $bansosHistory->status = DiskusiBansos::DONE;
                    $resultBansosHistory = $bansosHistory->create();
                }
            }
            //endregion

            $userData = UmUser::findFirst([
                "columns" => ["um_id", "foto", "presigned_url", "presigned_exp"],
                "conditions" => "um_id = :um_id:",
                "bind" => ["um_id" => $this->shared->user->um_id]
            ])->toArray();

            return new ObjectResponse(
                [
                    "success" => true,
                    "data" => [
                        "komentar_id" => $komentar->id,
                        "komentar" => $komentar->comment,
                        "uuid_user" => $this->shared->user->uuid_user,
                        "profile_picture_url" => MediaHelper::getPresignedUrlFoto($userData, UmUser::class, $this->config, $this->minio)
                    ]
                ]
            );
        } catch (ValidationException $e) {
            throw $e;
        } catch (CustomErrorException $e) {
            throw $e;
        } catch (\Exception $e) {
            throw new CustomErrorException(BaseResponse::UNEXPECTED_ERROR, $e->getMessage());
        }
    }

    public function kirimKomentarAttachment()
    {
        date_default_timezone_set('Asia/Jakarta');
        $this->checkScopes(['discussion.react']);

        $rawBody = $this->request->getPost();
        $rawBody["komentar_id"] = array_key_exists('komentar_id', $rawBody) ? $rawBody["komentar_id"] : 0;
        $validatedRequest = $this->validate(
            $rawBody,
            [
                'diskusi_id' => ['validators' => ['digit']],
                'komentar_id' => ['validators' => ['digit']],
                'confirm_all_status' => ['validators' => []],
                'confirm_assigned_status' => ['validators' => []],
                'follow_up_status' => ['validators' => []],
            ]
        );

        $level = 0;
        try {
            $commentOwnerId = 0;

            $diskusi = Diskusi::findFirst(
                [
                    "id = :id:",
                    "bind" => [
                        "id" => $rawBody["diskusi_id"]
                    ]
                ]
            );

            if ($diskusi->isDeleted()) {
                throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, 'Komentar gagal disimpan. Diskusi telah dikunci');
            }

            if (!empty($diskusi->report())) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Komentar gagal disimpan. Diskusi telah dilaporkan');
            }

            if ($diskusi->type == 0) {
                $type = "diskusi";
                $typeEn = "discussion";
            } else {
                $type = "cerita sukses";
                $typeEn = "success story";
            }

            $diskusiBansos = DiskusiBansos::findFirst(
                [
                    "deleted_at is null and id_diskusi = :id_diskusi:",
                    "bind" => [
                        "id_diskusi" => $diskusi->id
                    ]
                ]
            );

            $topik_type = '';
            if (!empty($diskusiBansos)) {
                $topik = TopikDiskusiBansos::findFirst([
                    'conditions' => 'deleted_at IS NULL and id = ?0',
                    'bind' => [
                        $diskusiBansos->id_topik
                    ]
                ]);
                $topik_type = empty($topik) ? TopikDiskusiBansos::TYPE_BANSOS : $topik->type;
                if ((!array_key_exists('follow_up_status', $validatedRequest) || empty($validatedRequest['follow_up_status']) || $validatedRequest['follow_up_status'] == 'null') &&
                    (
                        ($this->hasScopes(['bansos.follow_up']) && $topik_type == TopikDiskusiBansos::TYPE_BANSOS) ||
                        ($this->hasScopes(['covid.dinkes_pemda']) && $topik_type == TopikDiskusiBansos::TYPE_COVID) ||
                        ($this->hasScopes(['keluhan.ikn.follow_up']) && $topik_type == TopikDiskusiBansos::TYPE_IKN) ||
                        ($this->hasScopes(['keluhan.desa.follow_up']) && $topik_type == TopikDiskusiBansos::TYPE_DESA) ||
                        ($this->hasScopes(['keluhan.kampus.follow_up']) && $topik_type == TopikDiskusiBansos::TYPE_KAMPUS) ||
                        ($this->hasScopes(['keluhan.pelabuhan.follow_up']) && $topik_type == TopikDiskusiBansos::TYPE_PELABUHAN) ||
                        ($this->hasScopes(['keluhan.pendidikan.follow_up']) && $topik_type == TopikDiskusiBansos::TYPE_PENDIDIKAN)
                    ) &&
                    $diskusiBansos->status == DiskusiBansos::CONFIRMED
                ) {
                    throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Komentar gagal disimpan. Silahkan ubah status keluhan terlebih dahulu.');
                }
            }
            if ($rawBody["komentar_id"] != 0) {
                $komentarRes = DiskusiComment::find($rawBody["komentar_id"]);

                if (count($komentarRes) < 1) {
                    throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Komentar tidak ditemukan');
                }

                if ($komentarRes[0]->isDeleted()) {
                    throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Komentar telah dihapus');
                }

                if (!empty($komentarRes[0]->report())) {
                    throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Komentar telah dilaporkan');
                }

                if ($komentarRes->count() == 1) {
                    $sourceUser = $komentarRes[0]->umUser;
                    $level = $komentarRes[0]->level;
                    $level++;
                    $commentOwnerId = $komentarRes[0]->user_id;
                    if ($commentOwnerId != $this->shared->user->um_id && $commentOwnerId != $diskusi->user_id) {
                        $bodyId = "membalas komentar Anda dalam " . $type . " " . $diskusi->title;
                        $bodyEn = "replied your comment in " . $typeEn . " " . $diskusi->title;
                        $this->sendNotif($commentOwnerId, $this->shared->user->um_id, $bodyId, ($diskusi->type + 1), $diskusi->uuid, $bodyEn);
                        // todo uncomment di branch issue #1124
                        // $mailHelper = new MailHelper($this->config["mail_config"]);
                        // $mailHelper->mailDiskusiCommentReply($sourceUser->email, $this->shared->user->nama, $this->config["web_base_url"], $diskusi);
                    }
                }
            }

            if (array_key_exists('mentions', $rawBody) && !empty($rawBody["mentions"])) {
                if (is_string($rawBody["mentions"])) {
                    try {
                        $mentions = json_decode($rawBody["mentions"]);
                        if (!is_array($rawBody)) {
                            $mentions = explode(",", $rawBody["mentions"]);
                        }
                    } catch (\Exception $e) {
                        $mentions = [];
                    }
                } else if (is_array($rawBody["mentions"])) {
                    $mentions = array_unique($rawBody["mentions"]);
                } else {
                    $mentions = [];
                }
                $mentionWhere = "";
                $mentionI = 0;

                foreach ($mentions as $mention) {
                    if (strlen($mentionWhere) > 0) {
                        $mentionWhere .= ", ";
                    }
                    $mentionWhere .= ":mentions" . $mentionI . ":";
                    $mentionI++;
                    $resUser = UsersRoles::find(
                        [
                            "column" => "role_id",
                            "user_id = :user_id: and (role_id = 2 or role_id = 3)",
                            "bind" => [
                                "user_id" => $mention
                            ]
                        ]
                    );
                    if ($resUser->count() > 0) {
                        $diskusiMentionUser = new DiskusiMentionUser();
                        $diskusiMentionUser->setTransaction($this->transaction);
                        $diskusiMentionUser->user_id = $this->shared->user->um_id;
                        $diskusiMentionUser->tenant_id = -1234;
                        $diskusiMentionUser->user_mentioned_id = $mention;
                        $diskusiMentionUser->tenant_mentioned_id = -1234;
                        $diskusiMentionUser->diskusi_id = $diskusi->id;
                        $res = $diskusiMentionUser->save();

                        if (!$res) {
                            $this->transaction->rollback('Error while saving mentions');
                        }
                    }

                    if ($mention != $commentOwnerId && $mention != $diskusi->user_id) {
                        $bodyId = "menyebut Anda di komentar dalam " . $type . " " . $diskusi->title;
                        $bodyEn = "mentioned you in a comment in " . $typeEn . " " . $diskusi->title;
                        $this->sendNotif($mention, $this->shared->user->um_id, $bodyId, ($diskusi->type + 1), $diskusi->uuid, $bodyEn);
                    }
                }
            }
            $now = date('Y-m-d H:i:s');
            $komentar = new DiskusiComment();
            $komentar->setTransaction($this->transaction);

            $komentar->user_id = $this->shared->user->um_id;
            $komentar->diskusi_id = $rawBody["diskusi_id"];
            $komentar->level = $level;
            $komentar->comment = $rawBody["komentar"];
            $komentar->created_at = $now;
            $komentar->updated_at = $now;
            $komentar->comment_id = $rawBody["komentar_id"];
            $getDiskusiRespon = DiskusiRespon::findFirstByIdDiskusi($rawBody['diskusi_id']);
            if (empty($getDiskusiRespon->id_komentar_pertama)) {
                $komentarId = null;
            } else {
                $komentarId = -1;
            }

            $res = $komentar->save();

            $this->transaction->commit();

            $komentar = DiskusiComment::findFirst(
                [
                    "id = :id:",
                    "bind" => [
                        "id" => $komentar->id
                    ]
                ]
            );

            $this->checkMimeTypes();
            $subFolder =
                'comment_attachment' .
                DIRECTORY_SEPARATOR .
                $this->shared->user->um_id .
                DIRECTORY_SEPARATOR .
                $komentar->id;

            $komentar->commentAttachment = $this->processCommentAttachment($subFolder);

            $res = $komentar->save();

            $saveRespon = false;

            if (empty($komentarId)) {
                if (empty($getDiskusiRespon)) {
                    $getDiskusi = Diskusi::findFirst($rawBody['diskusi_id']);
                    $diskusi_date = new DateTime($getDiskusi->created_at);
                    $komentar_date = new DateTime($komentar->created_at);
                    $selisih = $diskusi_date->diff($komentar_date);
                    $interval = $selisih->y . ' tahun, ' . $selisih->m . ' bulan, ' . $selisih->d . ' hari, ' . $selisih->h . ' jam, ' . $selisih->i . ' menit, ' . $selisih->s . ' detik';

                    $setDiskusiRespon = new DiskusiRespon();
                    $setDiskusiRespon->title = $getDiskusi->title;
                    $setDiskusiRespon->id_diskusi = $getDiskusi->id;
                    $setDiskusiRespon->id_komentar_pertama = $komentar->id;
                    $setDiskusiRespon->id_sektor = $getDiskusi->kategori_diskusi_id;
                    $setDiskusiRespon->created_diskusi = $getDiskusi->created_at;
                    $setDiskusiRespon->created_komentar = $komentar->created_at;
                    $setDiskusiRespon->selisih = $interval;
                    $saveRespon = $setDiskusiRespon->save();
                } else {
                    $diskusi_date = new DateTime($getDiskusiRespon->created_diskusi);
                    $komentar_date = new DateTime($komentar->created_at);
                    $selisih = $diskusi_date->diff($komentar_date);
                    $interval = $selisih->y . ' tahun, ' . $selisih->m . ' bulan, ' . $selisih->d . ' hari, ' . $selisih->h . ' jam, ' . $selisih->i . ' menit, ' . $selisih->s . ' detik';

                    $getDiskusiRespon->id_komentar_pertama = $komentar->id;
                    $getDiskusiRespon->created_komentar = $komentar->created_at;
                    $getDiskusiRespon->selisih = $interval;
                    $saveRespon = $getDiskusiRespon->save();
                }
            }

            $getDiskusi = Diskusi::findByKategoriDiskusiId($diskusi->kategori_diskusi_id);
            $getJumlahDiskusi = DiskusiLikeComment::findFirstByIdSektor($diskusi->kategori_diskusi_id);
            if (empty($getJumlahDiskusi)) {
                $jmlhDiskusi = new DiskusiLikeComment();
                $jmlhDiskusi->id_sektor = $diskusi->kategori_diskusi_id;
                $jmlhDiskusi->jumlah_diskusi = count($getDiskusi);
                $diskusiLike = DiskusiLike::findByDiskusiId($rawBody["diskusi_id"]);
                $jmlhDiskusi->jumlah_like = count($diskusiLike);
                $result = 0;
                foreach ($getDiskusi as $gd) {
                    $diskusiComment = DiskusiComment::findByDiskusiId($gd->id);
                    $resultComment = count($diskusiComment);
                    $result += $resultComment;
                }
                $jmlhDiskusi->jumlah_komentar = $result;
                $saveKomentar = $jmlhDiskusi->save();
            } else {
                $result = 0;
                foreach ($getDiskusi as $gd) {
                    $diskusiComment = DiskusiComment::findByDiskusiId($gd->id);
                    $resultComment = count($diskusiComment);
                    $result += $resultComment;
                }
                $getJumlahDiskusi->jumlah_komentar = $result;
                $saveKomentar = $getJumlahDiskusi->save();
            }

            if (!$res && !$saveRespon && !$saveKomentar) {
                $this->transaction->rollback('Error while saving komentar');
            }

            if ($diskusi->user_id != $this->shared->user->um_id) {
                $diskusiBansos = DiskusiBansos::findFirst(
                    [
                        "deleted_at is null and id_diskusi = :id_diskusi:",
                        "bind" => [
                            "id_diskusi" => $diskusi->id
                        ]
                    ]
                );

                if (empty($diskusiBansos)) {
                    $bodyId = "memberikan komentar dalam diskusi " . $diskusi->title;
                    $bodyEn = "left comment in discussion " . $diskusi->title;
                    $this->sendNotif($diskusi->user_id, $this->shared->user->um_id, $bodyId, ($diskusi->type + 1), $diskusi->uuid, $bodyEn);
                } else {
                    if ($this->hasOneOfRoles(['admin_bansos_klop', 'superadmin_bansos', 'admin_bansos_korwil', 'admin_keluhan_ikn_klop', 'admin_keluhan_desa_klop', 'admin_keluhan_kampus_klop', 'admin_keluhan_pelabuhan_klop', 'admin_keluhan_bpjs_klop'])) {
                        $diskusiBansos->isresponded = 0;
                        $diskusiBansos->save();
                    }
                    $service = new DiskusiService($this->config, $this->shared, $this->request, $this->minio);
                    $service->notifyKorwilPemdaUser($diskusiBansos, DiskusiService::BANSOS_NOTIFICATION_COMMENT);
                }
            } else {
                $diskusiBansos = DiskusiBansos::findFirst(
                    [
                        "deleted_at is null and id_diskusi = :id_diskusi:",
                        "bind" => [
                            "id_diskusi" => $diskusi->id
                        ]
                    ]
                );
                if (!empty($diskusiBansos) && $diskusi->type == 0 && $diskusiBansos->status == 1) {
                    $diskusiBansos->isresponded = 1;
                    $diskusiBansos->save();
                }
            }

            //if ($diskusi->user_id != $this->shared->user->um_id)
            //    $this->sendNotif($diskusi->user_id, $this->shared->user->um_id, "memberikan komentar dalam diskusi " . $diskusi->title, ($diskusi->type + 1), $diskusi->uuid);

            $this->shared->user->jmlh_dukung_crt_mskn++;
            $this->shared->user->save();

            $bansosService = new BansosService($this);
            $bansosService->handleActivity($validatedRequest, $komentar);

            //region Handle flagging keluhan pemilu jika yg komen adalah Bawaslu/DKPP
            if ($this->hasScopes(['keluhan.pemilu.monitoring']) && $topik_type == TopikDiskusiBansos::TYPE_PEMILU) {
                if ($diskusiBansos->id_instansi == $this->shared->user->id_instansi) {
                    $keluhanPemilu = DiskusiKeluhanPemilu::findFirst([
                        'id_diskusi = ?0 AND id_diskusi_bansos = ?1',
                        'bind' => [
                            $diskusiBansos->id_diskusi,
                            $diskusiBansos->id,
                        ]
                    ]);
                    $keluhanPemilu->isresponded = 1;
                    $keluhanPemilu->updated_at = $now;
                    $keluhanPemilu->save();
                }
            }
            //endregion

            $userData = UmUser::findFirst([
                "columns" => ["um_id", "foto", "presigned_url", "presigned_exp"],
                "conditions" => "um_id = :um_id:",
                "bind" => ["um_id" => $this->shared->user->um_id]
            ])->toArray();

            return new ObjectResponse(
                [
                    "success" => true,
                    "data" => [
                        "komentar_id" => $komentar->id,
                        "komentar" => $komentar->comment,
                        "uuid_user" => $this->shared->user->uuid_user,
                        "profile_picture_url" => MediaHelper::getPresignedUrlFoto($userData, UmUser::class, $this->config, $this->minio)
                    ]
                ]
            );
        } catch (TxFailed $e) {
            throw new CustomErrorException(BaseResponse::SQL_ERROR, "SQL Error", $e->getMessage());
        }
    }

    public function editKomentar()
    {
        date_default_timezone_set('Asia/Jakarta');
        $this->checkScopes(['discussion.react']);
        /** @var UmUser $user */
        $user = $this->shared->user;
        $rawBody = $this->request->getJsonRawBody(true);
        $req = $this->validate(
            $rawBody,
            [
                'komentar_id' => ['validators' => ['required', 'digit']],
                'komentar' => ['validators' => ['required']]
            ]
        );
        $comment = DiskusiComment::findFirst([
            'id = :id:',
            'bind' => ['id' => $req['komentar_id']]
        ]);
        if ($user->um_id != $comment->user_id) {
            throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, "Anda tidak berhak mengakses.");
        }
        $now = date('Y-m-d H:i:s');
        $comment->comment = $req['komentar'];
        $comment->updated_at = $now;
        $comment->save();
        return new ObjectResponse([
            'success' => true,
            'data' => $comment->toArray()
        ]);
    }

    public function komentarDiskusi()
    {
        $params = $this->request->get();
        $validatedRequest = $this->validate(
            $params,
            [
                'diskusi_id' => ['validators' => ['required']]
            ]
        );

        $params['limit'] = $this->getDefaultLimit($params);
        $params["offset"] = array_key_exists("offset", $params) ? $params["offset"] : 0;

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

        $diskusi = Diskusi::findFirst(
            [
                "id = :diskusi_id:",
                "bind" => [
                    "diskusi_id" => $validatedRequest['diskusi_id']
                ]
            ]
        );

        $result = DiskusiComment::query()
            ->columns("DiskusiComment.id, UmUser.um_user_name as nama, UmUser.um_id, UmUser.presigned_url, UmUser.presigned_exp, UmUser.jenis_kelamin, UmUser.is_banned, DiskusiComment.created_at, DiskusiComment.updated_at, DiskusiComment.comment, DiskusiComment.level,
                            UmUser.uuid_user, DiskusiComment.is_deleted, UmUser.foto, DiskusiComment.diskusi_id, UmUser.checkmark_verified, UmUser.is_active as user_is_active")
            ->join('UmUser', 'DiskusiComment.user_id = UmUser.um_id')
            ->where("DiskusiComment.diskusi_id = :diskusi_id: AND DiskusiComment.level = 0")
            ->bind(
                [
                    "diskusi_id" => $params["diskusi_id"]
                ]
            )
            ->limit($params["limit"], $params["offset"])
            ->orderBy("DiskusiComment.id asc")
            ->execute()
            ->toArray();

        $i = 0;
        foreach ($result as $res) {
            $res["count_like"] = $this->diskusiCommentCountLike($res["id"]);
            $res["count_replies"] = $this->diskusiCommentCountReplies($res["id"]);
            if (isset($this->shared->user->um_id)) {
                $res["liked"] = DiskusiCommentLike::find(
                    [
                        "columns" => "count(1) as jumlah",
                        "user_id = :user_id: and comment_id = :comment_id:",
                        "bind" => [
                            "user_id" => $this->shared->user->um_id,
                            "comment_id" => $res["id"]
                        ]
                    ]
                )
                    ->toArray()[0]["jumlah"] == 1;
            } else {
                $res["liked"] = false;
            }
            if ($res["is_deleted"] == 1) {
                $res["comment"] = "";
            }

            if ($res['user_is_active']) {
                $res["attachment"] = CommentAttachment::find(
                    [
                        "columns" => "uuid, comment_id, id, filename, filetype, created_at",
                        "comment_id = :comment_id:",
                        "bind" => [
                            "comment_id" => $res["id"]
                        ]
                    ]
                )->toArray();

                if (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::MINIO) {
                    /** @var MinioService $minioService */
                    $minioService = $this->minio;
                    foreach ($res['attachment'] as $key => $val) {
                        $subFolder = 'comment_attachment' . DIRECTORY_SEPARATOR . $res['um_id'] . DIRECTORY_SEPARATOR . $val['comment_id'];
                        $filename = $val['filename'];
                        $filekey = $subFolder . DIRECTORY_SEPARATOR . $filename;

                        $url = $minioService->checkAndDownloadPrivate(
                            LogAttachments::CommentAttachment,
                            $val['created_at'],
                            $filekey,
                            $filename,
                            true
                        );

                        $res['attachment'][$key] = [
                            "id" => $val['id'],
                            "uuid" => $val['uuid'],
                            "filename" => $val['filename'],
                            "filetype" => $val['filetype'],
                            "url" => $url
                        ];
                    }
                }
            }

            $res["profile_picture_url"] = $res["foto"] == null || $res["foto"] == '' || $res["foto"] == '-' ? null : MediaHelper::getPresignedUrlFoto($res, UmUser::class, $this->config, $this->minio);
            unset($res["presigned_url"]);
            unset($res["presigned_exp"]);

            $res['laporan'] = DiskusiComment::getReport($res['id']);
            $res['laporan_user'] = DiskusiLapor::findFirst([
                'id_user_terlapor=:id: AND status=:status: AND deleted_at IS NULL',
                'bind' => [
                    'id' => $res['um_id'],
                    'status' => DiskusiLapor::STATUS_DISETUJUI
                ]
            ]);
            $res['diskusi_bansos_history'] = DiskusiBansosHistory::findFirst([
                'id_komentar=:id_komentar:',
                'bind' => [
                    'id_komentar' => $res['id']
                ]
            ]);

            if ($res['um_id'] == $diskusi->user_id && $diskusi->is_anonymous == 1) {
                $res['um_id'] = null;
                $res['nama'] = "Anonim";
                $res['uuid_user'] = "";
                $res["profile_picture_url"] = "";
            }

            if (!$res['user_is_active']) {
                $res['um_id'] = null;
                $res['nama'] = "Akun ini telah dihapus";
                $res['uuid_user'] = "";
                $res["profile_picture_url"] = "";
                $res["foto"] = null;
            }

            $censorResult = $this->getCensoredContent($res["comment"]);
            if ($censorResult['is_censored']) {
                $res["comment"] = $censorResult['censored_content'];
            }
            $res['is_censored'] = $censorResult['is_censored'];

            // $res['status_laporan_user'] = DiskusiComment::getStatusLaporanUser($res['id']);
            $result[$i] = $res;
            $i++;
        }

        $totalResult = DiskusiComment::query()
            ->columns("count(1) as jumlah")
            ->where("diskusi_id = :diskusi_id:")
            ->bind(
                [
                    "diskusi_id" => $params["diskusi_id"]
                ]
            )
            ->execute()
            ->toArray();

        return new PaginatedResponse($result, $totalResult[0]["jumlah"], $params["limit"], $params["offset"]);
    }

    public function komentarByUser()
    {
        $params = $this->request->get();
        $params['limit'] = $this->getDefaultLimit($params);
        $params["offset"] = array_key_exists("offset", $params) ? $params["offset"] : 0;

        $validatedRequest = $this->validate(
            $params,
            [
                'user_id' => ['validators' => ['required', 'digit']],
                'limit' => ['validators' => ['digit']],
                'offset' => ['validators' => ['digit']]
            ]
        );

        $result = [];
        $strWhere = 'DiskusiComment.user_id = :user_id: and 1 = CASE Diskusi.user_id WHEN DiskusiComment.user_id THEN CASE Diskusi.is_anonymous WHEN 0 THEN 1 ELSE 0 END ELSE 1 END';
        $bind['user_id'] = $params['user_id'];
        $query = DiskusiComment::query()
            ->join('Diskusi', 'Diskusi.id = DiskusiComment.diskusi_id')
            ->where($strWhere)
            ->bind($bind);

        $totalResult = $query
            ->columns('COUNT(1) as counter')
            ->execute()[0]['counter'];

        $res = $query
            ->columns('DiskusiComment.*')
            ->limit($validatedRequest["limit"], $validatedRequest["offset"])
            ->execute();

        for ($i = 0; $i < count($res); $i++) {
            $result[] = $this->getArrayCommentComplete($res[$i]);
        }

        return new PaginatedResponse($result, $totalResult, $params["limit"], $params["offset"]);
    }

    private function getArrayCommentComplete($data)
    {
        $result = $data->toArray();
        $result['diskusi'] = Diskusi::query()
            ->columns("Diskusi.uuid, KategoriDiskusi.produk_id")
            ->join("KategoriDiskusi", "KategoriDiskusi.id = Diskusi.kategori_diskusi_id")
            ->where("Diskusi.id = :id_diskusi:")
            ->bind([
                'id_diskusi' => $result['diskusi_id']
            ])
            ->limit(1)
            ->execute()[0];
        $result['owner'] = UmUser::findFirst(
            [
                "um_id = :user_id:",
                "bind" => [
                    "user_id" => $result['user_id']
                ]
            ]
        )->toArraySimple();
        $result["count_like"] = DiskusiCommentLike::count([
            'comment_id = :id:',
            'bind' => [
                'id' => $result['id']
            ]
        ]);
        $result['laporan'] = DiskusiLapor::findFirst([
            "id_komentar=:id_komentar: AND status = :status: AND id_user_terlapor IS NULL",
            'bind' => [
                'id_komentar' => $result['id'],
                'status' => DiskusiLapor::STATUS_DISETUJUI
            ]
        ]);

        return $result;
    }

    public function komentarReplies($id)
    {
        $result = DiskusiComment::query()
            ->columns("DiskusiComment.id, DiskusiComment.comment_id, DiskusiComment.diskusi_id, UmUser.um_user_name as nama, DiskusiComment.updated_at, DiskusiComment.comment, DiskusiComment.level,
                            UmUser.uuid_user, DiskusiComment.is_deleted, UmUser.foto, UmUser.jenis_kelamin, UmUser.is_banned, UmUser.um_id, UmUser.presigned_url, UmUser.presigned_exp")
            ->join('UmUser', 'DiskusiComment.user_id = UmUser.um_id')
            ->where("DiskusiComment.comment_id = :comment_id:")
            ->bind(
                [
                    "comment_id" => $id
                ]
            )
            ->orderBy("DiskusiComment.id asc")
            ->execute()
            ->toArray();

        if (count($result) > 0) {
            $diskusi = Diskusi::findFirst(
                [
                    "id = :diskusi_id:",
                    "bind" => [
                        "diskusi_id" => $result[0]['diskusi_id']
                    ]
                ]
            );
        }

        $i = 0;
        foreach ($result as $res) {
            $res["count_like"] = $this->diskusiCommentCountLike($res["id"]);
            $res["count_replies"] = $this->diskusiCommentCountReplies($res["id"]);
            if (isset($this->shared->user->um_id)) {
                $res["liked"] = DiskusiCommentLike::find(
                    [
                        "columns" => "count(1) as jumlah",
                        "user_id = :user_id: and comment_id = :comment_id:",
                        "bind" => [
                            "user_id" => $this->shared->user->um_id,
                            "comment_id" => $res["id"]
                        ]
                    ]
                )
                    ->toArray()[0]["jumlah"] == 1;
            } else {
                $res["liked"] = false;
            }
            if ($res["is_deleted"] == 1) {
                $res["comment"] = "";
            }

            $res["attachment"] = CommentAttachment::find(
                [
                    "columns" => "uuid, id, filename, filetype, created_at",
                    "comment_id = :comment_id:",
                    "bind" => [
                        "comment_id" => $res["id"]
                    ]
                ]
            )->toArray();

            if (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::MINIO) {
                $subFolder = 'comment_attachment' . DIRECTORY_SEPARATOR . $res['um_id'] . DIRECTORY_SEPARATOR . $res['id'];
                /** @var MinioService $minioService */
                $minioService = $this->minio;
                foreach ($res['attachment'] as $key => $val) {
                    $filename = $val['filename'];
                    $filekey = $subFolder . DIRECTORY_SEPARATOR . $filename;

                    $url = $minioService->checkAndDownloadPrivate(
                        LogAttachments::CommentAttachment,
                        $val['created_at'],
                        $filekey,
                        $filename,
                        true
                    );

                    $res['attachment'][$key] = [
                        "id" => $val['id'],
                        "uuid" => $val['uuid'],
                        "filename" => $val['filename'],
                        "filetype" => $val['filetype'],
                        "url" => $url
                    ];
                }
            }

            $res["profile_picture_url"] = $res["foto"] == null || $res["foto"] == '' || $res["foto"] == '-' ? null : MediaHelper::getPresignedUrlFoto($res, UmUser::class, $this->config, $this->minio);
            unset($res["presigned_url"]);
            unset($res["presigned_exp"]);

            $res['laporan'] = DiskusiComment::getReport($res['id']);
            $res['laporan_user'] = DiskusiLapor::findFirst([
                'status=:status: AND id_user_terlapor =:user_id: AND deleted_at IS NULL',
                'bind' => [
                    'status' => DiskusiLapor::STATUS_DISETUJUI,
                    'user_id' => $res['um_id']
                ]
            ]);
            $res['diskusi_bansos_history'] = DiskusiBansosHistory::findFirst([
                'id_komentar=:id_komentar:',
                'bind' => [
                    'id_komentar' => $res['id']
                ]
            ]);

            if ($res['um_id'] == $diskusi->user_id && $diskusi->is_anonymous == 1) {
                $res['um_id'] = null;
                $res['nama'] = "Anonim";
                $res['uuid_user'] = "";
                $res["profile_picture_url"] = "";
            }

            $censorResult = $this->getCensoredContent($res["comment"]);
            if ($censorResult['is_censored']) {
                $res["comment"] = $censorResult['censored_content'];
            }
            $res['is_censored'] = $censorResult['is_censored'];

            $result[$i] = $res;
            $i++;
        }

        return new CollectionPaginatedResponse($result);
    }

    public function komentarDetail($komentar_id)
    {
        $validatedRequest = $this->validate(
            [
                'komentar_id' => $komentar_id
            ],
            [
                'komentar_id' => ['validators' => ['required', 'numeric']]
            ]
        );

        $result = DiskusiComment::query()
            ->columns("DiskusiComment.id, UmUser.um_id, UmUser.um_user_name as nama, DiskusiComment.created_at, DiskusiComment.updated_at, DiskusiComment.comment, DiskusiComment.level,
                            UmUser.uuid_user, DiskusiComment.is_deleted, UmUser.foto, UmUser.presigned_url, UmUser.presigned_exp")
            ->join('UmUser', 'DiskusiComment.user_id = UmUser.um_id')
            ->where("DiskusiComment.id = :komentar_id:")
            ->bind(
                [
                    "komentar_id" => $komentar_id
                ]
            )
            ->execute()
            ->toArray();

        $i = 0;

        foreach ($result as $res) {
            $res["count_like"] = $this->diskusiCommentCountLike($res["id"]);
            if (isset($this->shared->user->um_id)) {
                $res["liked"] = DiskusiCommentLike::find(
                    [
                        "columns" => "count(1) as jumlah",
                        "user_id = :user_id: and comment_id = :comment_id:",
                        "bind" => [
                            "user_id" => $this->shared->user->um_id,
                            "comment_id" => $res["id"]
                        ]
                    ]
                )
                    ->toArray()[0]["jumlah"] == 1;
            } else {
                $res["liked"] = false;
            }
            if ($res["is_deleted"] == 1) {
                $res["comment"] = "";
            }

            $res["attachment"] = CommentAttachment::find(
                [
                    "columns" => "uuid, comment_id, filename, filetype, created_at",
                    "comment_id = :comment_id:",
                    "bind" => [
                        "comment_id" => $res["id"]
                    ]
                ]
            )->toArray();

            if (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::MINIO) {
                /** @var MinioService $minioService */
                $minioService = $this->minio;
                foreach ($res['attachment'] as $key => $val) {
                    $subFolder = 'comment_attachment' . DIRECTORY_SEPARATOR . $res['um_id'] . DIRECTORY_SEPARATOR . $val['comment_id'];
                    $filename = $val['filename'];
                    $filekey = $subFolder . DIRECTORY_SEPARATOR . $filename;

                    $url = $minioService->checkAndDownloadPrivate(
                        LogAttachments::CommentAttachment,
                        $val['created_at'],
                        $filekey,
                        $filename,
                        true
                    );

                    $res['attachment'][$key] = [
                        //"id" => $val['id'],
                        "uuid" => $val['uuid'],
                        "filename" => $val['filename'],
                        "filetype" => $val['filetype'],
                        "url" => $url
                    ];
                }
            }

            $res["profile_picture_url"] = $res["foto"] == null || $res["foto"] == '' || $res["foto"] == '-' ? null : MediaHelper::getPresignedUrlFoto($result[$i], UmUser::class, $this->config, $this->minio);
            unset($res["presigned_url"]);
            unset($res["presigned_exp"]);

            $result[$i] = $res;
            $i++;
        }

        return new CollectionPaginatedResponse($result);
    }

    public function hapusKomentar($id)
    {
        date_default_timezone_set('Asia/Jakarta');
        $this->checkScopes(['discussion.react']);

        $comment = DiskusiComment::findFirst(
            [
                "user_id = :user_id: and id = :id:",
                "bind" => [
                    "user_id" => $this->shared->user->um_id,
                    "id" => $id
                ]
            ]
        );

        if ($comment) {
            $comment->is_deleted = 1;
            $comment->updated_at = date('Y-m-d H:i:s');
            $comment->save();
        } else {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Komentar tidak ditemukan.");
        }

        return new ObjectResponse(
            [
                "success" => true
            ]
        );
    }

    public function likeList($uuid)
    {
        $this->checkIsAuthenticated();

        $diskusiList = Diskusi::findByUuid($uuid);

        if (count($diskusiList) < 1) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, 'Diskusi tidak ditemukan');
        }

        $likes = DiskusiLike::findByDiskusiId($diskusiList[0]->id);

        $result = [];
        foreach ($likes as $like) {
            $row = $like->toArrayWithUser();

            if (array_key_exists('user', $row)) {
                $row['user']['profile_picture_url'] = MediaHelper::getPresignedUrlFoto($row['user'], UmUser::class, $this->config, $this->minio);
                unset($row['user']["presigned_url"]);
                unset($row['user']["presigned_exp"]);
            }

            $result[] = $row;
        }

        return new CollectionPaginatedResponse($result);
    }

    public function diskusiLike()
    {
        $this->checkScopes(['discussion.react']);

        $rawBody = $this->request->getJsonRawBody(true);
        $rawBody["komentar_id"] = array_key_exists('komentar_id', $rawBody) ? $rawBody["komentar_id"] : 0;
        $rawBody["diskusi_id"] = array_key_exists('diskusi_id', $rawBody) ? $rawBody["diskusi_id"] : 0;

        $validatedRequest = $this->validate(
            $rawBody,
            [
                'diskusi_id' => ['validators' => ['digit']],
                'komentar_id' => ['validators' => ['digit']]
            ]
        );

        try {
            $this->db->begin();
            $result = [];
            if ($rawBody["diskusi_id"] > 0) {
                $diskusi = Diskusi::findFirst(
                    [
                        "id = :id:",
                        "bind" => [
                            "id" => $rawBody["diskusi_id"]
                        ]
                    ]
                );

                if ($diskusi->isDeleted()) {
                    throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Diskusi telah dihapus');
                }

                if (!empty($diskusi->report())) {
                    throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Diskusi telah dilaporkan');
                }

                // handle waktu voting Mascot Design Challenge
                if ($diskusi->kategori_diskusi_id == getenv('PRODUK_MASCOT_CHALLENGE_ID')) {
                    date_default_timezone_set('Asia/Jakarta');
                    $now = date('Y-m-d H:i:s');
                    $start = date('Y-m-d H:i:s', strtotime(getenv("VOTING_MASCOT_CHALLENGE_START")));
                    $end = date('Y-m-d H:i:s', strtotime(getenv("VOTING_MASCOT_CHALLENGE_END")));
                    if ($now <= $start || $now > $end) {
                        $dtMulai = DateTime::createFromFormat("Y-m-d H:i:s", $start);
                        $tglMulai = $dtMulai->format("d") . " " . DateHelper::convertMonthDigittoString($dtMulai->format("m")) . " " . $dtMulai->format("Y") . " " . $dtMulai->format("H:i:s");
                        $dtSelesai = DateTime::createFromFormat("Y-m-d H:i:s", $end);
                        $tglSelesai = $dtSelesai->format("d") . " " . DateHelper::convertMonthDigittoString($dtSelesai->format("m")) . " " . $dtSelesai->format("Y") . " " . $dtSelesai->format("H:i:s");
                        throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Waktu pemilihan dimulai pada " . $tglMulai . " sampai " . $tglSelesai);
                    }
                }

                $deleted = false;

                $resLike = DiskusiLike::find(
                    [
                        "user_id = :user_id: and diskusi_id = :diskusi_id:",
                        "bind" => [
                            "user_id" => $this->shared->user->um_id,
                            "diskusi_id" => $rawBody["diskusi_id"]
                        ]
                    ]
                );
                if ($resLike->count() > 0) {
                    $resLike->delete();
                    $deleted = true;
                    if ($diskusi->user_id != $this->shared->user->um_id) {
                        $diskusi->umUser->jmlh_dukung_crt_mskn--;
                        if ($diskusi->umUser->jmlh_dukung_crt_mskn < 0) {
                            $diskusi->umUser->jmlh_dukung_crt_mskn = 0;
                        }
                        $diskusi->umUser->save();
                    }

                    $getDiskusi = Diskusi::findFirst($rawBody["diskusi_id"]);
                    $getSektorDiskusi = Diskusi::findByKategoriDiskusiId($getDiskusi->kategori_diskusi_id);
                    $resultTotal = 0;
                    foreach ($getSektorDiskusi as $gsd) {
                        $getDiskusiLike = DiskusiLike::findByDiskusiId($gsd->id);
                        $resultLike = count($getDiskusiLike);
                        $resultTotal += $resultLike;
                    }

                    $sektorDiskusi = DiskusiLikeComment::findFirstByIdSektor($getDiskusi->kategori_diskusi_id);
                    $sektorDiskusi->jumlah_like = $resultTotal;
                    $sektorDiskusi->save();
                } else {
                    $like = new DiskusiLike();
                    $like->user_id = $this->shared->user->um_id;
                    $like->diskusi_id = $rawBody["diskusi_id"];
                    $like->save();

                    $getDiskusi = Diskusi::findFirst($rawBody["diskusi_id"]);
                    $sektorDiskusi = DiskusiLikeComment::findFirstByIdSektor($getDiskusi->kategori_diskusi_id);
                    if (!empty($sektorDiskusi)) {
                        $getSektorDiskusi = Diskusi::findByKategoriDiskusiId($getDiskusi->kategori_diskusi_id);
                        $resultTotal = 0;
                        foreach ($getSektorDiskusi as $gsd) {
                            $getDiskusiLike = DiskusiLike::findByDiskusiId($gsd->id);
                            $resultLike = count($getDiskusiLike);
                            $resultTotal += $resultLike;
                        }
                        $sektorDiskusi->jumlah_like = $resultTotal;
                        $sektorDiskusi->save();
                    } else {
                        $diskusiTotal = Diskusi::find([
                            "conditions" => "kategori_diskusi_id=:id_kategori:",
                            "bind" => [
                                "id_kategori" => $getDiskusi->kategori_diskusi_id
                            ]
                        ]);
                        $jmlhLike = new DiskusiLikeComment();
                        $jmlhLike->id_sektor = $getDiskusi->kategori_diskusi_id;
                        $jmlhLike->jumlah_diskusi = count($diskusiTotal);
                        $jmlhLike->jumlah_like = 1;
                        $jmlhLike->save();
                    }

                    if ($diskusi->type == 0) {
                        $type = "diskusi";
                        $typeEn = "discussion";
                    } else {
                        $type = "cerita sukses";
                        $typeEn = "success story";
                    }
                    if ($diskusi->user_id != $like->user_id) {
                        $diskusi->umUser->jmlh_dukung_crt_mskn++;
                        $diskusi->umUser->save();
                        $bodyId = "menyukai " . $type . " " . $diskusi->title;
                        $bodyEn = "liked " . $typeEn . " " . $diskusi->title;
                        $this->sendNotif($diskusi->user_id, $this->shared->user->um_id, $bodyId, ($diskusi->type + 1), $diskusi->uuid, $bodyEn);
                    }
                }

                $result = [
                    "success" => true,
                    "data" => [
                        "liked" => !$deleted,
                        "count_like" => $this->diskusiCountLike($rawBody["diskusi_id"])
                    ]
                ];
            } else {
                $komentar = DiskusiComment::findFirst($rawBody["komentar_id"]);

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

                if ($komentar->isDeleted()) {
                    throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Komentar telah dihapus');
                }

                if (!empty($komentar->report())) {
                    throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Komentar telah dilaporkan');
                }

                $resLike = DiskusiCommentLike::find(
                    [
                        "user_id = :user_id: and comment_id = :comment_id:",
                        "bind" => [
                            "user_id" => $this->shared->user->um_id,
                            "comment_id" => $rawBody["komentar_id"]
                        ]
                    ]
                );

                $deleted = false;
                if ($resLike->count() > 0) {
                    $resLike->delete();
                    $deleted = true;
                } else {
                    $like = new DiskusiCommentLike();
                    $like->user_id = $this->shared->user->um_id;
                    $like->comment_id = $rawBody["komentar_id"];
                    $like->save();

                    $diskusi = $komentar->diskusi;
                    if ($diskusi->type == 0) {
                        $type = "diskusi";
                        $typeEn = "discussion";
                    } else {
                        $type = "cerita sukses";
                        $typeEn = "success story";
                    }
                    if ($komentar->user_id != $like->user_id) {
                        $bodyId = "menyukai komentar Anda dalam " . $type . " " . $diskusi->title;
                        $bodyEn = "liked your comment " . $typeEn . " " . $diskusi->title;
                        $this->sendNotif($komentar->user_id, $this->shared->user->um_id, $bodyId, ($diskusi->type + 1), $diskusi->uuid, $bodyEn);
                    }
                }

                $result = [
                    "success" => true,
                    "data" => [
                        "liked" => !$deleted,
                        "count_like" => $this->diskusiCommentCountLike($rawBody["komentar_id"])
                    ]
                ];
            }
            $this->db->commit();
            return new ObjectResponse($result);
        } catch (\Exception $e) {
            $this->db->rollback();
            throw new CustomErrorException(BaseResponse::UNEXPECTED_ERROR, $e->getMessage());
        }
    }

    public function listForAdmin()
    {
        $this->checkScopes(['admin.view']);
        $params = $this->request->get();

        $params['limit'] = $this->getDefaultLimit($params);
        $params["offset"] = array_key_exists("offset", $params) ? $params["offset"] : 0;
        $params["keyword"] = array_key_exists("keyword", $params) ? $params["keyword"] : null;
        $params["type"] = array_key_exists("type", $params) ? $params["type"] : 0;
        $params["order_by"] = array_key_exists("order_by", $params) ? $params["order_by"] : 'release_date';
        $params["order_direction"] = array_key_exists("order_direction", $params) ? $params["order_direction"] : 'asc';
        $params["status"] = array_key_exists("status", $params) ? $params["status"] : 0;
        $params["tags"] = array_key_exists("tags", $params) ? $params["tags"] : null;

        $allowed_order = [
            'id',
            'title',
            'updated_at',
            'release_date',
        ];

        $allowed_order_direction = ['asc', 'desc'];

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

        $condition = '(lower(title) like :keyword: OR lower(content) like :keyword: OR user_id in (select x.um_id from UmUser x where lower(um_user_name) like :keyword: or lower(nama) like :keyword:)) AND type = :type: AND is_deleted = :status:';
        $bind = ['keyword' => "%" . strtolower($validatedParams['keyword']) . "%", 'type' => $validatedParams['type'], 'status' => $params['status'] == 0 ? 0 : 1];

        if (!empty($validatedParams['tags']) && $validatedParams['tags'] != null && $validatedParams['type'] == '1') {
            $tags = explode('|', $validatedParams['tags']);
            $idDikusi = array_column(
                BeritaTags::query()
                    ->where('id_tags in ({idtags:array})')
                    ->bind([
                        'idtags' => array_values($tags)
                    ])
                    ->execute()
                    ->toArray(),
                'id_diskusi'
            );
            if (empty($idDikusi)) {
                $condition .= " AND FALSE"; //artinya tidak ada diskusi dengan tags tersebut
            } else {
                $condition .= " AND id in ({iddiskusi:array})";
                $bind['iddiskusi'] = array_values($idDikusi);
            }
        }

        $diskusi_list = Diskusi::query()
            ->where($condition)
            ->bind($bind)
            ->limit($params["limit"], $params["offset"])
            ->orderBy($params["order_by"] . ' ' . $params["order_direction"])
            ->execute();

        $result = [];

        foreach ($diskusi_list as $diskusi) {
            $row = $diskusi->toArray();
            if ($validatedParams['type'] == '1')
                $row['tags'] = $diskusi->tags->toArray();
            /** @var UmUser $creator */
            $creator = $diskusi->UmUser;

            if (!empty($creator)) {
                $row['user'] = $creator->toArraySimpleWithFoto();
                $row['user']['profile_picture_url'] = MediaHelper::getPresignedUrlFoto($row['user'], UmUser::class, $this->config, $this->minio);
            }

            $laporans = DiskusiLapor::find([
                "id_diskusi=:id_diskusi: AND id_komentar IS NULL",
                'bind' => [
                    'id_diskusi' => $diskusi->id
                ]
            ]);

            $row['laporans'] = $laporans;

            $result[] = $row;
        }

        $totalResult = Diskusi::count([
            $condition,
            'bind' => $bind
        ]);

        return new PaginatedResponse($result, $totalResult, $params["limit"], $params["offset"]);
    }

    public function exportForAdmin()
    {
        $this->checkScopes(['admin.view']);
        $params = $this->request->get();

        $params["start_date"] = array_key_exists("start_date", $params) ? $params["start_date"] : date('Y-m-d', strtotime('-1 week'));
        $params["end_date"] = array_key_exists("end_date", $params) ? $params["end_date"] : date('Y-m-d', strtotime('+1 day'));
        $params["keyword"] = array_key_exists("keyword", $params) ? $params["keyword"] : null;
        $params["type"] = array_key_exists("type", $params) ? $params["type"] : 0;
        $params["product_id"] = array_key_exists("product_id", $params) ? $params["product_id"] : -1;
        $params["order_by"] = array_key_exists("order_by", $params) ? $params["order_by"] : 'updated_at';
        $params["order_direction"] = array_key_exists("order_direction", $params) ? $params["order_direction"] : 'desc';
        $params["status"] = array_key_exists("status", $params) ? $params["status"] : 0;

        $allowed_order = [
            'id',
            'title',
            'updated_at',
            'release_date',
        ];

        $allowed_order_direction = ['asc', 'desc'];

        $validatedParams = $this->validate(
            $params,
            [
                'start_date' => ['validators' => ['date']],
                'end_date' => ['validators' => ['date']],
                'keyword' => ['validators' => []],
                'type' => ['validators' => ['digit']],
                'product_id' => ['validators' => ['digit']],
                'order_by' => ['validators' =>  [[
                    'name' => 'inclusion_in',
                    'params' => $allowed_order
                ]]],
                'order_direction' => ['validators' =>  [[
                    'name' => 'inclusion_in',
                    'params' => $allowed_order_direction
                ]]]

            ]
        );

        $params['order_by'] = 'Diskusi.' . $params['order_by'];

        $condition = '(lower(Diskusi.title) like :keyword: OR lower(Diskusi.content) like :keyword: OR Diskusi.user_id in (select x.um_id from UmUser x where lower(um_user_name) like :keyword: or lower(UmUser.nama) like :keyword:)) AND Diskusi.type = :type: AND Diskusi.is_deleted = :status: AND Diskusi.created_at >= :start_date: AND Diskusi.created_at <= :end_date:';
        $bind = ['keyword' => "%" . strtolower($validatedParams['keyword']) . "%", 'type' => $validatedParams['type'], 'status' => $params['status'] == 0 ? 0 : 1, 'start_date' => $params['start_date'], 'end_date' => $params['end_date']];

        if ($params['product_id'] > 0) {
            $kategoriDiskusi = KategoriDiskusi::findFirst(
                [
                    "produk_id = :product_id:",
                    "bind" => [
                        "product_id" => $params['product_id']
                    ]
                ]
            );

            if (empty($kategoriDiskusi)) {
                throw new ValidationException([
                    [
                        'field' => 'produk_id',
                        'message' => 'Kategori diskusi dengan produk(' . $params['product_id'] . ') tidak ditemukan.'
                    ]
                ]);
            }

            $condition .= " AND Diskusi.kategori_diskusi_id = :kategori_diskusi_id:";
            $bind['kategori_diskusi_id'] = $kategoriDiskusi->id;
        }

        $diskusi_list = Diskusi::query()
            ->columns("UmUser.nama as nama_user, COALESCE(TopikDiskusiBansos.deskripsi, '-') as topik_diskusi, COALESCE(DiskusiObjek.nama_objek, '-') as subjek_diskusi, Produk.nama as kategori, COALESCE(Diskusi.link_terkait, '-') as link_terkait, Diskusi.title as judul, Diskusi.content as konten_diskusi, CASE Diskusi.is_deleted WHEN 0 THEN 'Aktif' ELSE 'Tidak Aktif' END as status_diskusi, CASE Diskusi.is_anonymous WHEN 1 THEN 'Ya' ELSE 'Tidak' END as status_anonim, substring(cast(Diskusi.created_at as varchar),0,20) as tanggal_diskusi, CASE COUNT(Comment.id) WHEN 0 THEN '' ELSE string_agg(concat('[', coalesce(UserComment.nama, 'Anonim'), '] [', substring(cast(Comment.updated_at as varchar),0,20), '] : ', Comment.comment), ';\r') END as komentar, count(Comment.comment) as jumlah_komentar")
            ->join('UmUser', 'Diskusi.user_id = UmUser.um_id')
            ->join('KategoriDiskusi', 'Diskusi.kategori_diskusi_id = KategoriDiskusi.id')
            ->join('Produk', 'KategoriDiskusi.produk_id = Produk.id_index')
            ->leftjoin('DiskusiBansos', 'Diskusi.id = DiskusiBansos.id_diskusi')
            ->leftjoin('TopikDiskusiBansos', 'DiskusiBansos.id_topik = TopikDiskusiBansos.id')
            ->leftjoin('DiskusiObjek', 'Diskusi.id = DiskusiObjek.diskusi_id')
            ->leftjoin('DiskusiComment', 'Comment.diskusi_id = Diskusi.id', 'Comment')
            ->leftjoin('UmUser', 'UserComment.um_id = Comment.user_id', 'UserComment')
            ->where($condition)
            ->bind($bind)
            ->orderBy($params["order_by"] . ' ' . $params["order_direction"])
            ->groupBy('UmUser.nama, TopikDiskusiBansos.deskripsi, DiskusiObjek.nama_objek, Produk.nama, Diskusi.link_terkait, Diskusi.title, Diskusi.content, Diskusi.is_deleted, Diskusi.is_anonymous, Diskusi.created_at, ' . $params['order_by'])
            ->execute()->toArray();

        #region Generate Excel File

        $spreadsheet = $this->generateExcelListDiskusi($diskusi_list);
        $todayLabel = DateHelper::formatToday();

        $writer = new Xlsx($spreadsheet);
        $filename = "Export Laporan Diskusi - $todayLabel.xlsx";

        header('Content-type: application/vnd.ms-excel');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        return $writer->save('php://output');
        //return $diskusi_list;

        #endregion
    }

    private function generateExcelListDiskusi($items)
    {
        $spreadsheet = new Spreadsheet();
        $sheet = $spreadsheet->getActiveSheet();

        $todayLabel = DateHelper::formatToday();
        $dateNow = Carbon::now();

        $sheet->setCellValue("A1", 'Daftar Diskusi');
        $sheet->setCellValue("A2", $todayLabel);

        $sheet->getStyle('A1:A2')
            ->getAlignment()
            ->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
        $sheet->mergeCells('A1:L1');
        $sheet->mergeCells('A2:L2');

        $sheet->setCellValue("A4", 'No');
        $sheet->setCellValue("B4", 'Author');
        $sheet->setCellValue("C4", 'Kategori');
        $sheet->setCellValue("D4", 'Topik Diskusi');
        $sheet->setCellValue("E4", 'Subjek Diskusi');
        $sheet->setCellValue("F4", 'Link Terkait');
        $sheet->setCellValue("G4", 'Judul Diskusi');
        $sheet->setCellValue("H4", 'Konten Diskusi');
        $sheet->setCellValue("I4", 'Status Diskusi');
        $sheet->setCellValue("J4", 'Status Anonim');
        $sheet->setCellValue("K4", 'Komentar');
        $sheet->setCellValue("L4", 'Jumlah Komentar');

        $sheet->getStyle('A4:L4')
            ->getAlignment()
            ->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);

        $row = 5;
        $index = 1;

        foreach ($items as $item) {

            $sheet->setCellValue("A$row", $index);
            $sheet->setCellValue("B$row", $item['nama_user']);
            $sheet->setCellValue("C$row", $item['topik_diskusi']);
            $sheet->setCellValue("D$row", $item['subjek_diskusi']);
            $sheet->setCellValue("E$row", $item['kategori']);
            $sheet->setCellValue("F$row", $item['link_terkait']);
            $sheet->setCellValue("G$row", $item['judul']);
            $sheet->setCellValue("H$row", $item['konten_diskusi']);
            $sheet->setCellValue("I$row", $item['status_diskusi']);
            $sheet->setCellValue("J$row", $item['status_anonim']);
            $sheet->setCellValueExplicit("K$row", $item['komentar'], \PHPOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
            $sheet->setCellValue("L$row", $item['jumlah_komentar']);

            $index++;
            $row++;
        }

        $sheet->getColumnDimension('A')->setAutoSize(true);
        $sheet->getColumnDimension('B')->setAutoSize(true);
        $sheet->getColumnDimension('C')->setWidth(40);
        $sheet->getColumnDimension('D')->setAutoSize(true);
        $sheet->getColumnDimension('E')->setAutoSize(true);
        $sheet->getColumnDimension('F')->setAutoSize(true);
        $sheet->getColumnDimension('G')->setAutoSize(true);
        $sheet->getColumnDimension('H')->setWidth(40);
        $sheet->getColumnDimension('I')->setAutoSize(true);
        $sheet->getColumnDimension('J')->setAutoSize(true);
        $sheet->getColumnDimension('K')->setWidth(40);
        $sheet->getColumnDimension('L')->setAutoSize(true);


        $styleArray = [
            'borders' => [
                'allBorders' => [
                    'borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN,
                    'color' => ['argb' => 'FF000000'],
                ],
            ],
        ];

        $sheet->getStyle('A4:L' . ($row - 1))->applyFromArray($styleArray);
        $sheet->getStyle('C4:C' . ($row - 1))->getAlignment()->setWrapText(true);
        $sheet->getStyle('H4:H' . ($row - 1))->getAlignment()->setWrapText(true);
        $sheet->getStyle('K4:K' . ($row - 1))->getAlignment()->setWrapText(true);

        return $spreadsheet;
    }

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

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

        $params['diskusi_id'] = array_key_exists('diskusi_id', $postParams) ? $postParams['diskusi_id'] : null;

        $validatedParams = $this->validate(
            $params,
            [
                'diskusi_id' => ['validators' => ['required']],
            ]
        );

        $diskusi = Diskusi::findFirst([
            'conditions' => 'id = :diskusi_id:',
            'bind' => ['diskusi_id' => $validatedParams['diskusi_id']]
        ]);

        if (empty($diskusi)) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, 'Diskusi not found');
        }

        if ($diskusi->isDeleted()) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, 'Diskusi telah dikunci');
        }

        // if (!empty($diskusi->report())) {
        //     throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Diskusi telah dilaporkan');
        // }

        $diskusi->is_deleted = 1;
        $diskusi->save();

        return new ObjectResponse([
            'success' => true,
            'data' => $diskusi
        ]);
    }

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

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

        $params['diskusi_id'] = array_key_exists('diskusi_id', $postParams) ? $postParams['diskusi_id'] : null;

        $validatedParams = $this->validate(
            $params,
            [
                'diskusi_id' => ['validators' => ['required']],
            ]
        );

        $diskusi = Diskusi::findFirst([
            'conditions' => 'id = :diskusi_id:',
            'bind' => ['diskusi_id' => $validatedParams['diskusi_id']]
        ]);

        if (empty($diskusi)) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, 'Diskusi not found');
        }

        if ($diskusi->is_deleted < 1) {
            throw new CustomErrorException(400, 'Diskusi already active');
        }

        $diskusi->is_deleted = 0;
        $diskusi->save();

        return new ObjectResponse([
            'success' => true,
            'data' => $diskusi
        ]);
    }

    public function downloadAttachment($uuid)
    {
        if ($uuid == 'undefined')
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Parameter UUID tidak boleh "undefined"!');

        $getParams = $this->request->get();
        $params = $this->validate(
            $getParams,
            [
                'is_inline' => ['validators' => []]
            ]
        );

        $attachment = DiskusiAttachment::findFirst(
            [
                "uuid = :uuid:",
                "bind" => [
                    "uuid" => $uuid
                ]
            ]
        );

        if ($attachment) {
            if (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::MINIO) {
                $subFolder = "diskusi_attachment" . DIRECTORY_SEPARATOR . $attachment->diskusi->user_id . DIRECTORY_SEPARATOR . $attachment->diskusi->id;
                /** @var MinioService $minioService */
                $minioService = $this->minio;
                $filename = $attachment->filename;
                $filekey = $subFolder . DIRECTORY_SEPARATOR . $filename;
                $isDownload = !$params['is_inline'];

                $url = $minioService->checkAndDownloadPrivate(
                    LogAttachments::DiskusiAttachment,
                    $attachment->created_at,
                    $filekey,
                    $filename,
                    $isDownload
                );

                $response = new \Phalcon\Http\Response();
                return $response->redirect($url, true, 302);
            } elseif (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::LOCAL) {
                $subFolder = getenv("PRIVATE_FOLDER") . DIRECTORY_SEPARATOR . 'diskusi_attachment' . DIRECTORY_SEPARATOR . $attachment->diskusi->user_id . DIRECTORY_SEPARATOR . $attachment->diskusi->id;
                $filename = $subFolder . DIRECTORY_SEPARATOR . $attachment->filename;

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

                return MediaHelper::fileResponse($filename, null, true, $params['is_inline']);
            }
        } else {
            return new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Attachment Not Found");
        }
    }

    public function downloadCommentAttachment($uuid)
    {
        /** @var CommentAttachment $attachment */
        $attachment = CommentAttachment::findFirst(
            [
                "uuid = :uuid:",
                "bind" => [
                    "uuid" => $uuid
                ]
            ]
        );
        if ($attachment) {
            if (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::MINIO) {
                $subFolder = 'comment_attachment' . DIRECTORY_SEPARATOR . $attachment->diskusiComment->user_id . DIRECTORY_SEPARATOR . $attachment->comment_id;

                /** @var MinioService $minioService */
                $minioService = $this->minio;
                $filename = $attachment->filename;
                $filekey = $subFolder . DIRECTORY_SEPARATOR . $filename;

                $url = $minioService->checkAndDownloadPrivate(
                    LogAttachments::CommentAttachment,
                    $attachment->created_at,
                    $filekey,
                    $filename,
                    true
                );

                $response = new \Phalcon\Http\Response();
                return $response->redirect($url, true, 302);
            } elseif (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::LOCAL) {
                $subFolder = getenv("PRIVATE_FOLDER") . DIRECTORY_SEPARATOR . "comment_attachment" . $attachment->diskusiComment->user_id . DIRECTORY_SEPARATOR . $attachment->comment_id;
                $filename = $subFolder . DIRECTORY_SEPARATOR . $attachment->filename;

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

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


    public function viewCommentAttachment($uuid)
    {
        /** @var CommentAttachment $attachment */
        $attachment = CommentAttachment::findFirst(
            [
                "uuid = :uuid:",
                "bind" => [
                    "uuid" => $uuid
                ]
            ]
        );

        if ($attachment) {
            if (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::MINIO) {
                $subFolder = 'comment_attachment' . DIRECTORY_SEPARATOR . $attachment->diskusiComment->user_id . DIRECTORY_SEPARATOR . $attachment->comment_id;
                /** @var MinioService $minioService */
                $minioService = $this->minio;
                $filename = $attachment->filename;
                $filekey = $subFolder . DIRECTORY_SEPARATOR . $filename;

                $url = $minioService->checkAndDownloadPrivate(
                    LogAttachments::CommentAttachment,
                    $attachment->created_at,
                    $filekey,
                    $filename,
                    false
                );

                $response = new \Phalcon\Http\Response();
                return $response->redirect($url, true, 302);
            } elseif (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::LOCAL) {
                $subFolder = getenv('PRIVATE_FOLDER') . 'comment_attachment' . DIRECTORY_SEPARATOR . $attachment->diskusiComment->user_id . DIRECTORY_SEPARATOR . $attachment->comment_id;
                $filename = $subFolder . DIRECTORY_SEPARATOR . $attachment->filename;

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

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

    public function diskusiUser()
    {
        $params = $this->request->get();

        $params["produk_id"] = array_key_exists("produk_id", $params) ? $params["produk_id"] : 0;
        $params['limit'] = $this->getDefaultLimit($params);
        $params["offset"] = array_key_exists("offset", $params) ? $params["offset"] : 0;

        $this->validate(
            $params,
            [
                'user_id' => ['validators' => ['required', 'digit']],
                'produk_id' => ['validators' => ['digit']],
                'limit' => ['validators' => ['digit']],
                'offset' => ['validators' => ['digit']]
            ]
        );

        if ($params["produk_id"] == 0) {
            $strWhere = "Produk.id_index != :produk_id: and Produk.id_index != :keluhan_produk_id:";

            $bind = [
                "produk_id" => $params["produk_id"],
                "keluhan_produk_id" => 9
            ];
        } else {
            $strWhere = "Produk.id_index = :produk_id:";

            $bind = [
                "produk_id" => $params["produk_id"]
            ];
        }

        $strWhere .= " and Diskusi.user_id = :user_id:";

        $strWhere .= " and type = 0 and is_deleted = 0 and is_anonymous = 0";

        $bind["user_id"] = $params["user_id"];

        $query = Diskusi::query()
            ->columns("Diskusi.id, Diskusi.uuid, Diskusi.title, Diskusi.content, Diskusi.created_at, Diskusi.updated_at, UmUser.um_user_name as nama, UmUser.uuid_user, Produk.nama as nama_produk")
            ->join('UmUser', 'Diskusi.user_id = UmUser.um_id')
            ->join('KategoriDiskusi', 'Diskusi.kategori_diskusi_id = KategoriDiskusi.id')
            ->join('Produk', 'KategoriDiskusi.produk_id = Produk.id_index');

        $queryCount = Diskusi::query()
            ->columns("count(1) as jumlah")
            ->join('UmUser', 'Diskusi.user_id = UmUser.um_id')
            ->join('KategoriDiskusi', 'Diskusi.kategori_diskusi_id = KategoriDiskusi.id')
            ->join('Produk', 'KategoriDiskusi.produk_id = Produk.id_index');


        $orderBy = "Diskusi.updated_at desc";

        $result = $query
            ->where($strWhere)
            ->bind($bind)
            ->limit($params["limit"], $params["offset"])
            ->orderBy($orderBy)
            ->execute()
            ->toArray();

        for ($i = 0; $i < count($result); $i++) {
            $diskusi_id = $result[$i]["id"];
            $result[$i]['count_like'] = DiskusiLike::find("diskusi_id='$diskusi_id'")->count();
            $result[$i]['count_comment'] = DiskusiComment::find("diskusi_id='$diskusi_id'")->count();
            $report = Diskusi::getReport($result[$i]['id']);
            if (empty($report)) {
                $result[$i]['laporan'] = null;
            } else {
                $result[$i]['laporan'] = $report->simpleToArray();
            }
        }


        $totalCount = $queryCount
            ->where($strWhere)
            ->bind($bind)
            ->execute()
            ->toArray();

        return new PaginatedResponse($result, $totalCount[0]["jumlah"], $params["limit"], $params["offset"]);
    }

    public function diskusiShare()
    {
        $rawBody = $this->request->getJsonRawBody(true);
        $rawBody["diskusi_id"] = array_key_exists('diskusi_id', $rawBody) ? $rawBody["diskusi_id"] : 0;
        $rawBody["type"] = array_key_exists('type', $rawBody) ? $rawBody["type"] : 0;

        $validatedRequest = $this->validate(
            $rawBody,
            [
                'diskusi_id' => ['validators' => ['required', 'digit']],
                'type' => ['validators' => ['required']]
            ]
        );

        if ($rawBody["diskusi_id"] > 0) {
            $diskusi = Diskusi::findFirst(
                [
                    "id = :id:",
                    "bind" => [
                        "id" => $rawBody["diskusi_id"]
                    ]
                ]
            );

            if ($diskusi->isDeleted()) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Diskusi telah dihapus');
            }

            if (!empty($diskusi->report())) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Diskusi telah dilaporkan');
            }

            $share = new DiskusiShare();
            $share->user_id = ($this->shared && array_key_exists('user', $this->shared)) ? $this->shared->user->um_id : 0;
            $share->diskusi_id = $rawBody["diskusi_id"];
            $share->type = $rawBody["type"];
            $success = $share->save();

            return new ObjectResponse([
                'success' => $success,
                'data' => $share
            ]);
        }
    }

    public function editModerasiDiskusi($uuid)
    {
        date_default_timezone_set('Asia/Jakarta');
        $this->checkOneOfScopes(['admin.view', 'bansos.confirm_all', 'covid.dinkes_pemda']);

        $params = $this->request->getPost();

        $params["is_attachment_public"] = array_key_exists('is_attachment_public', $params) ? $params["is_attachment_public"] : 1;
        $validations = [
            'id' => ['validators' => []],
            'produk_id' => ['validators' => ['required']],
            'objek_id' => ['validators' => ['required']],
            'objek_type' => ['validators' => []],
            'nama_objek' => ['validators' => []],
            'is_attachment_public' => ['validators' => ['required']]
        ];
        if (array_key_exists('produk_id', $params) && $params['produk_id'] == Produk::PRODUK_ID_BANSOS) {
            $validations = array_merge($validations, [
                'id_topik' => ['validators' => ['required', 'digit']]
            ]);
        }
        $validatedParams = $this->validate(
            $params,
            $validations
        );

        $topikDiskusiBansos = null;
        if (array_key_exists('id_topik', $validatedParams)) {
            $topikDiskusiBansos = TopikDiskusiBansos::findFirst([
                "conditions" => 'id = :id:',
                "bind" => [
                    'id' => $validatedParams['id_topik']
                ]
            ]);
            if (!$topikDiskusiBansos) {
                throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Topik Keluhan tidak ditemukan");
            }
        }

        $result = Diskusi::find([
            "conditions" => "uuid = :uuid:",
            "bind" => [
                'uuid' => $uuid
            ]
        ]);

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

        $diskusi = $result[0];

        if ($diskusi->isDeleted()) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, 'Diskusi telah dikunci');
        }

        if (!empty($diskusi->report())) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Diskusi telah dilaporkan');
        }

        $produkResult = Produk::findFirst([
            "conditions" => "id_index = :id:",
            "bind" => [
                "id" => $validatedParams['produk_id']
            ]
        ]);

        if (empty($produkResult)) {
            throw new ValidationException([
                [
                    'field' => 'produk_id',
                    'message' => 'Produk tidak ditemukan'
                ]
            ]);
        }

        $kategoriDiskusi = KategoriDiskusi::findFirst([
            "conditions" => "produk_id = :produk_id:",
            "bind" => [
                "produk_id" => $validatedParams['produk_id']
            ]
        ]);

        if (empty($kategoriDiskusi)) {
            throw new ValidationException([
                [
                    'field' => 'produk_id',
                    'message' => 'Kategori Diskusi tidak ditemukan'
                ]
            ]);
        }

        $kategoriCurrent = KategoriDiskusi::findFirst([
            "conditions" => "id = :id:",
            "bind" => [
                "id" => $diskusi->kategori_diskusi_id
            ]
        ]);

        if (!empty($kategoriCurrent)) {
            $produkCurrent = Produk::findFirst([
                "conditions" => "id_index = :id:",
                "bind" => [
                    "id" => $kategoriCurrent->produk_id
                ]
            ]);

            if (($produkCurrent->id_index == Produk::PRODUK_ID_BANSOS && $produkResult->id_index != Produk::PRODUK_ID_BANSOS) ||
                ($produkCurrent->id_index != Produk::PRODUK_ID_BANSOS && $produkResult->id_index == Produk::PRODUK_ID_BANSOS)
            ) {
                throw new ValidationException([
                    [
                        'field' => 'produk_id',
                        'message' => 'Tidak dapat mengubah dari Keluhan Bansos ke Kategori Diskusi lainnya.'
                    ]
                ]);
            }
        }
        if (empty($validatedParams['objek_type']) && $topikDiskusiBansos->type === TopikDiskusiBansos::TYPE_PENDIDIKAN) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Tipe Objek keluhan harus diisi");
        }
        $this->checkMimeTypes();

        $diskusiId = $diskusi->id;

        $diskusiObjekExist = DiskusiObjek::findFirstByDiskusiId($diskusiId);
        $diskusiBansosExist = DiskusiBansos::findFirstByIdDiskusi($diskusiId);

        $now = date('Y-m-d H:i:s');
        $diskusi->kategori_diskusi_id = $kategoriDiskusi->id;
        $diskusi->updated_at = $now;
        $diskusi->is_attachment_public = !empty($diskusiBansosExist) ? $validatedParams['is_attachment_public'] : 0;
        $diskusi->save();

        if (!empty($diskusiObjekExist)) {
            $diskusiObjekExist->diskusi_id = $diskusiId;
            $diskusiObjekExist->nama_objek = $validatedParams['nama_objek'];
            $diskusiObjekExist->objek_id = $validatedParams['objek_id'];
            if (!empty($validatedParams['objek_type'])) {
                $diskusiObjekExist->objek_type = $validatedParams['objek_type'];
            }
            $diskusiObjekExist->updated_at = $now;
            $diskusiObjekExist->save();
        } else {
            $diskusi->diskusiObjek = new DiskusiObjek();
            $diskusi->diskusiObjek->diskusi_id = $diskusiId;
            $diskusi->diskusiObjek->nama_objek = $validatedParams['nama_objek'];
            $diskusi->diskusiObjek->objek_id = $validatedParams['objek_id'];
            if (!empty($validatedParams['objek_type'])) {
                $diskusi->objek_type = $validatedParams['objek_type'];
            }
            $diskusi->diskusiObjek->save();
        }
        if (!empty($diskusiBansosExist)) {
            $isChangeTopic = $diskusiBansosExist->id_topik != $validatedParams['id_topik'];

            // untuk type topik keluhan desa, instansi merupakan inspektorat kabupaten sesuai lokasi desa yang menjadi objek keluhan
            if ($topikDiskusiBansos->type === TopikDiskusiBansos::TYPE_DESA) {
                $objectDesa = OmspanMasterDesa::findFirst('id = ' . $validatedParams["objek_id"]);
                if (empty($objectDesa)) {
                    throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Objek keluhan Desa tidak ditemukan");
                }

                $instansiDesa = Instansi::findFirst([
                    'conditions' => 'id_kota_kabupaten = :kode_pemda: AND id_tipe = 2 AND deleted_at IS NULL',
                    'bind' => [
                        'kode_pemda' => $objectDesa->kode_pemda
                    ]
                ]);
                if (empty($instansiDesa)) {
                    throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Inspektorat Kabupaten Desa tidak ditemukan");
                }
                $diskusiBansosExist->id_instansi = $instansiDesa->id;
                if ($diskusiBansosExist->status == DiskusiBansos::NEW) {
                    $diskusiBansosExist->id_instansi_origin = $instansiDesa->id;
                }
            } elseif($topikDiskusiBansos->type !== TopikDiskusiBansos::TYPE_PENDIDIKAN) { // untuk type topik keluhan pendidikan, tidak ada relasi antara objek keluhan dengan instansi
                $diskusiBansosExist->id_instansi = $validatedParams['objek_id'];
                if ($diskusiBansosExist->status == DiskusiBansos::NEW) {
                    $diskusiBansosExist->id_instansi_origin = $validatedParams['objek_id'];
                }
            }
            $diskusiBansosExist->id_topik = $validatedParams['id_topik'];
            $diskusiBansosExist->save();

            $diskusiBansosHistory = new DiskusiBansosHistory();
            $diskusiBansosHistory->id_diskusi_bansos = $diskusiBansosExist->id;
            $diskusiBansosHistory->keterangan = $isChangeTopic ? 'Moderasi Perubahan Topik Keluhan' : 'Moderasi Perubahan Objek Keluhan';
            $diskusiBansosHistory->status = $diskusiBansosExist->status;
            $diskusiBansosHistory->id_instansi = $diskusiBansosExist->id_instansi;
            $diskusiBansosHistory->id_user = $this->shared->user->um_id;
            $diskusiBansosHistory->create();
        }
        return new ObjectResponse(
            [
                "success" => true,
                "data" => [
                    "diskusi_uuid" => $diskusi->uuid,
                    "diskusi_id" => $diskusi->id
                ]
            ]
        );
    }

    public function originalContent()
    {
        date_default_timezone_set('Asia/Jakarta');
        $this->checkIsAuthenticated();
        $params = $this->request->get();

        $validatedRequest = $this->validate(
            $params,
            [
                'uuid' => ['validators' => ['required']],
            ]
        );

        $diskusi = Diskusi::findFirst([
            'conditions' => "Diskusi.uuid = :uuid:",
            'bind' => [
                "uuid" => $validatedRequest['uuid']
            ]
        ]);

        if (empty($diskusi)) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, 'Diskusi tidak ditemukan');
        } else {
            if ($diskusi->user_id != $this->shared->user->um_id) {
                $this->checkOneOfScopes(['bansos.view_all', 'bansos.view_assigned', 'bansos.confirm_all', 'bansos.confirm_assigned', 'bansos.follow_up', 'keluhan.ikn.follow_up', 'keluhan.desa.follow_up', 'keluhan.pendidikan.follow_up']);
            }

            $result['original_content'] = $diskusi->content;

            return new PaginatedResponse([$result], 1, 1, 0);
        }
    }

    public function originalComment()
    {
        date_default_timezone_set('Asia/Jakarta');
        $this->checkIsAuthenticated();
        $params = $this->request->get();

        $validatedRequest = $this->validate(
            $params,
            [
                'comment_id' => ['validators' => ['required']],
                'is_censored' => ['validators' => []]
            ]
        );

        $comment = DiskusiComment::findFirst(
            [
                'id = :comment_id:',
                'bind' => [
                    'comment_id' => $validatedRequest['comment_id']
                ]
            ]
        );

        if (empty($comment)) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, 'Komentar tidak ditemukan');
        } else {
            if ($comment->user_id != $this->shared->user->um_id) {
                $this->checkOneOfScopes(['bansos.view_all', 'bansos.view_assigned', 'bansos.confirm_all', 'bansos.confirm_assigned', 'bansos.follow_up', 'keluhan.ikn.follow_up', 'keluhan.desa.follow_up', 'keluhan.pendidikan.follow_up']);
            }

            if (!is_null($validatedRequest['is_censored']) && $validatedRequest['is_censored'] == '1') {
                $censorResult = $this->getCensoredContent($comment->comment);
                if ($censorResult['is_censored']) {
                    $result['original_comment'] = $censorResult['censored_content'];
                } else {
                    $result['original_comment'] = $comment->comment;
                }
            } else {
                $result['original_comment'] = $comment->comment;
            }

            return new PaginatedResponse([$result], 1, 1, 0);
        }
    }

    private function getCensoredContent($konten)
    {
        $result['is_censored'] = false;
        $nikPattern = "/[0-9]{16}/";
        $phonePattern = "/08+[0-9]{8,10}/";

        if (
            preg_match($nikPattern, $konten) ||
            preg_match($phonePattern, $konten)
        ) {
            $result['is_censored'] = true;
            $result['censored_content'] = preg_replace(
                $phonePattern,
                '[08***]',
                preg_replace($nikPattern, '[***]', $konten)
            );
        }

        return $result;
    }

    public function editDiskusiAttachment($uuid)
    {
        $this->checkScopes(['admin.view']);
        $request = $this->request->getJsonRawBody(true);
        $validatedRequest = $this->validate($request, [
            'caption' => ['validators' => []]
        ]);
        /** @var DiskusiAttachment $attachment */
        $attachment = DiskusiAttachment::findFirst(['conditions' => 'uuid = :uuid:', 'bind' => ['uuid' => $uuid]]);
        if (!$attachment) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Attachment [$uuid] tidak ditemukan.");
        }
        $attachment->caption = $validatedRequest['caption'];
        $res = $attachment->save();
        if (!$res) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Gagal menyimpan attachment");
        }

        return new SimpleResponse($attachment);
    }

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

        $produk = Produk::findFirst($id_index);

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

        $produk->is_show = 1;
        return new SimpleResponse($produk->save());
    }

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

        $produk = Produk::findFirst($id_index);

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

        $produk->is_show = 0;
        return new SimpleResponse($produk->save());
    }

    public function getLatest()
    {
        $result = [
            "diskusi" => $this->getLatestDiscussion(),
            "berita" => $this->getLatestNews()
        ];

        return new SimpleResponse($result);
    }

    private function getLatestDiscussion()
    {
        $discussions = $this->rawQuery("
            SELECT DISTINCT ON (diskusi.id) diskusi.uuid, diskusi.title, diskusi.title_en, diskusi.content, diskusi.created_at, um_user.jenis_kelamin, produk.nama AS nama_produk, um_user.um_id, um_user.foto, um_user.presigned_url, um_user.presigned_exp, diskusi.is_anonymous, 
                   ((SELECT count(*) FROM jaga.diskusi_comment AS x WHERE x.diskusi_id = diskusi.id)) AS total_comment,
                   ((SELECT count(*) FROM jaga.diskusi_share AS x WHERE x.diskusi_id = diskusi.id)) AS total_share, 
                   ((SELECT count(*) FROM jaga.diskusi_like AS x WHERE x.diskusi_id = diskusi.id)) AS total_like 
            FROM jaga.diskusi
            LEFT JOIN jaga.diskusi_lapor ON diskusi_lapor.id_diskusi = diskusi.id
            LEFT JOIN jaga.um_user ON diskusi.user_id = um_user.um_id 
            LEFT JOIN jaga.kategori_diskusi ON diskusi.kategori_diskusi_id = kategori_diskusi.id 
            LEFT JOIN jaga.produk ON kategori_diskusi.produk_id = produk.id_index 
            WHERE produk.id_index <> 0 AND produk.is_active = TRUE AND diskusi.type = 0 AND diskusi.is_deleted = 0 AND um_user.is_banned = 'f' AND diskusi_lapor.id_diskusi IS NULL 
            ORDER BY diskusi.id DESC LIMIT 6 OFFSET 0
        ");
        $discussions = $this->handleAnonymousCondition($discussions);
        return $this->sanitizeContent($discussions);
    }
    private function getLatestNews()
    {
        try {
            $tz = 'Asia/Jakarta';
            $timestamp = time();
            $dt = new DateTime("now", new DateTimeZone($tz));
            $dt->setTimestamp($timestamp);
            $conditions = " and (release_date is not null and release_date <= '" . $dt->format('Y-m-d H:i:s') . "')";
        } catch (Exception $e) {
            $conditions = " and (release_date is not null and release_date <= '" . date("Y-m-d H:i:s") . "')";
        }
        $news = $this->rawQuery("
            SELECT diskusi.id, diskusi.uuid, diskusi.title, diskusi.title_en, diskusi.release_date, produk.nama AS nama_produk, 
                   ((SELECT count(*) FROM jaga.diskusi_comment AS x WHERE x.diskusi_id = diskusi.id)) AS total_comment,
                   ((SELECT count(*) FROM jaga.diskusi_share AS x WHERE x.diskusi_id = diskusi.id)) AS total_share, 
                   ((SELECT count(*) FROM jaga.diskusi_like AS x WHERE x.diskusi_id = diskusi.id)) AS total_like 
            FROM jaga.diskusi
            LEFT JOIN jaga.um_user ON diskusi.user_id = um_user.um_id 
            LEFT JOIN jaga.kategori_diskusi ON diskusi.kategori_diskusi_id = kategori_diskusi.id 
            LEFT JOIN jaga.produk ON kategori_diskusi.produk_id = produk.id_index 
            WHERE diskusi.type = 1 AND diskusi.is_deleted = 0 $conditions
            ORDER BY diskusi.release_date DESC LIMIT 6 OFFSET 0
        ");

        return $this->retrieveNewsAttachmentAndSetSlug($news);
    }

    private function handleAnonymousCondition($discussions)
    {
        for ($i = 0; $i < count($discussions); $i++) {
            if ($discussions[$i]["is_anonymous"] == 1) {
                $discussions[$i]["nama"] = "Anonim";
                $discussions[$i]["profile_picture_url"] = null;
            } else {
                $discussions[$i]["profile_picture_url"] = $discussions[$i]["foto"] == null || $discussions[$i]["foto"] == '' || $discussions[$i]["foto"] == '-' ? null : MediaHelper::getPresignedUrlFoto($discussions[$i], UmUser::class, $this->config, $this->minio);
            }
        }
        return $discussions;
    }

    private function sanitizeContent($discussions)
    {
        $contentLength = 500;
        $filter = new Filter();

        for ($i = 0; $i < count($discussions); $i++) {
            $discussions[$i]["content"] = $filter->sanitize($discussions[$i]["content"], 'striptags');

            $censorResult  = $this->getCensoredContent($discussions[$i]["content"]);
            if ($censorResult['is_censored']) {
                $discussions[$i]["content"] = $censorResult['censored_content'];
            }
            $discussions[$i]['is_censored'] = $censorResult['is_censored'];

            if (strlen($discussions[$i]["content"]) >= $contentLength) {
                $discussions[$i]["content"] = mb_substr($discussions[$i]["content"], 0, ($contentLength - 3)) . "...";
            }

            unset($discussions[$i]["presigned_url"]);
            unset($discussions[$i]["presigned_exp"]);
        }

        return $discussions;
    }

    private function retrieveNewsAttachmentAndSetSlug($news)
    {
        for ($i = 0; $i < count($news); $i++) {
            $imageUuid = DiskusiAttachment::find(
                [
                    "columns" => "uuid",
                    "diskusi_id = :diskusi_id:",
                    "bind" => [
                        "diskusi_id" => $news[$i]["id"]
                    ],
                    "limit" => 1
                ]
            )
                ->toArray();

            if (count($imageUuid) > 0) {
                $news[$i]["image_uuid"] = $imageUuid[0]["uuid"];
            }

            $news[$i]["slug"] = CommonTools::slug($news[$i]["title"]);
        }
        return $news;
    }

    public function retreiveBerita()
    {
        $request = $this->request->get();
        $validatedRequest = $this->validate(
            $request,
            [
                'slug' => ['validators' => ['required']],
                'uuid' => ['validators' => []]
            ]
        );
        $slug = $validatedRequest['slug'];
        $keyword = Diskusi::slugToQuery($slug);

        $conditions = "Diskusi.title ilike :keyword: and Diskusi.type = 1";
        if (!$this->hasScopes(['admin.view'])) $conditions .= " and Diskusi.is_deleted = 0";
        $bind = ['keyword' => $keyword];
        if (!empty($validatedRequest['uuid'])) {
            $conditions .= " and Diskusi.uuid = :uuid:";
            $bind['uuid'] = $validatedRequest['uuid'];
        }
        $diskusi = Diskusi::findFirst([
            'conditions' => $conditions,
            'bind' => $bind
        ]);

        if (empty($diskusi)) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, 'Berita tidak ditemukan');
        }

        $diskusi->views += 1;
        $diskusi->save();

        $data = $diskusi->toJsonForDetail(null, $this->config["profile_picture_base_url"], $this->config, $this->minio);

        $data['tags'] = $diskusi->tags->toArray();

        //Rekam UserActivity
        if ($this->shared) {
            $service = new ActivityService($this->shared, $this->logger);
            $service->createActivity($data['produk_id'], $data['id_objek'], $diskusi->id);
        }
        if (isset($this->shared->user->um_id)) {
            $data["liked"] = DiskusiLike::count(
                [
                    "diskusi_id = :diskusi_id: and user_id = :user_id:",
                    "bind" => [
                        "diskusi_id" => $diskusi->id,
                        "user_id" => $this->shared->user->um_id
                    ]
                ]
            ) > 0;
        } else {
            $data["liked"] = false;
        }

        $censorResult = $this->getCensoredContent($data["content"]);
        if ($censorResult['is_censored']) {
            $data["content"] = $censorResult['censored_content'];
        }
        $data['is_censored'] = $censorResult['is_censored'];

        return new PaginatedResponse([$data], 1, 1, 0);
    }

    public function dashboardPublic()
    {
        $request = $this->request->get();

        $request['years'] = array_key_exists('years', $request) ? $request['years'] : [date("Y")];
        $request['product'] = array_key_exists('product', $request) ? $request['product'] : null;

        $params = $this->validate(
            $request,
            [
                'years' => ['validators' => []],
                'product' => ['validators' => [
                    'required',
                    [
                        'name' => 'inclusion_in',
                        'params' => ['diskusi', 'keluhan']
                    ]
                ]]
            ]
        );

        $where1 = 'd.type = 0 AND d.is_deleted = 0';
        $binds1 = [];

        $where2 = $where1;

        if (!empty($params['product']) && $params['product'] === 'diskusi') {
            $where1 .= ' AND p.id_index <> 9 AND p.is_active = TRUE';
            $where2 = $where1;
        }

        if (!empty($params['product']) && $params['product'] === 'keluhan') {
            $where1 .= " AND p.id_index = 9 AND p.is_active = TRUE AND tdb.type <> 'BANSOS' AND tdb.type <> 'COVID'";
            $where2 = $where1;
        }

        if (!empty($params['years'])) {
            $years = '{' . implode(',', $params['years']) . '}';
            $where1 .= ' AND EXTRACT(YEAR FROM d.created_at) = ANY(:years)';
            $binds1['years'] = $years;
        }

        $select = "trim(split_part(tdb.deskripsi, '-', 1)) as nama, COUNT(d.id) as total";
        $groupBy = "tdb.deskripsi";

        if ($params['product'] === 'diskusi') {
            $select = "p.nama, COUNT(d.id) as total";
            $groupBy = "p.nama";
        }

        $discussionByTheme = $this->rawQuery("
            SELECT
                nama, SUM(total) AS total
            FROM (            
                SELECT
                    $select
                FROM
                    jaga.produk p
                LEFT JOIN
                    jaga.kategori_diskusi kd ON kd.produk_id = p.id_index
                LEFT JOIN
                    jaga.diskusi d ON d.kategori_diskusi_id = kd.id
                LEFT JOIN
                    jaga.diskusi_bansos db ON db.id_diskusi = d.id
                LEFT JOIN
                    jaga.topik_diskusi_bansos tdb ON tdb.id = db.id_topik
                WHERE
                    $where1
                GROUP BY
                    $groupBy
                ORDER BY
                    total DESC
            ) AS sub
            GROUP BY
                nama
        ", $binds1);

        $totalAllDiscussion = $this->rawQuery("
            SELECT
                COUNT(d.id) as total
            FROM
                jaga.produk p
            LEFT JOIN
                jaga.kategori_diskusi kd ON kd.produk_id = p.id_index
            LEFT JOIN
                jaga.diskusi d ON d.kategori_diskusi_id = kd.id
            LEFT JOIN
                jaga.diskusi_bansos db ON db.id_diskusi = d.id
            LEFT JOIN
                jaga.topik_diskusi_bansos tdb ON tdb.id = db.id_topik
            WHERE
                $where1
        ", $binds1);

        $discussionByTheme[] = [
            'nama' => 'Total',
            'total' => $totalAllDiscussion[0]['total']
        ];

        $years = $this->rawQuery("
            SELECT DISTINCT
                EXTRACT(YEAR FROM d.created_at) AS label,
                EXTRACT(YEAR FROM d.created_at) AS value
            FROM
                jaga.produk p
            LEFT JOIN
                jaga.kategori_diskusi kd ON kd.produk_id = p.id_index
            LEFT JOIN
                jaga.diskusi d ON d.kategori_diskusi_id = kd.id
            LEFT JOIN
                jaga.diskusi_bansos db ON db.id_diskusi = d.id
            LEFT JOIN
                jaga.topik_diskusi_bansos tdb ON tdb.id = db.id_topik
            WHERE
                $where2
            ORDER BY
                value DESC;
        ");

        return new SimpleResponse([
            'years' => $years,
            'table' => $discussionByTheme,
            'chart' => !empty($params['years']) && count($params['years']) === 1 && $params['years'][0] === date("Y") ?
                $this->buildChartDashboardPublicByYear([date("Y")], $where2) : $this->buildChartDashboardPublicByYear($params['years'], $where2)
        ]);
    }

    public function buildChartDashboardPublicByYear($years, $where)
    {
        $monthsIn = [];
        foreach ($years as $year) {
            $monthsIn[$year] = array_fill(0, 12, 0);
        }

        $discussions = $this->rawQuery(
            "
            SELECT
                TO_CHAR(d.created_at, 'YYYY') AS year,
                TO_CHAR(d.created_at, 'MM') AS month,
                COUNT(d.id) AS total
            FROM
                jaga.produk p
            LEFT JOIN
                jaga.kategori_diskusi kd ON kd.produk_id = p.id_index
            LEFT JOIN
                jaga.diskusi d ON d.kategori_diskusi_id = kd.id
            LEFT JOIN
                jaga.diskusi_bansos db ON db.id_diskusi = d.id
            LEFT JOIN
                jaga.topik_diskusi_bansos tdb ON tdb.id = db.id_topik
            WHERE
                $where AND
                TO_CHAR(d.created_at, 'YYYY') = ANY(:years)
            GROUP BY
                year, month
            ORDER BY
                year, month
            ",
            ['years' => '{' . implode(',', $years) . '}']
        );

        if (count($discussions) > 0) {
            foreach ($discussions as $discussion) {
                if (intval($discussion['total']) > 0) {
                    $yearIndex = $discussion['year'];
                    $monthIndex = intval($discussion['month']) - 1;
                    $monthsIn[$yearIndex][$monthIndex] = intval($discussion['total']);
                }
            }
        }

        $result = [];
        foreach ($monthsIn as $year => $data) {
            $result[] = [
                'name' => $year,
                'data' => $data
            ];
        }

        return $result;
    }

    public function chartDashboardByName()
    {
        $request = $this->request->get();

        $request['years'] = array_key_exists('years', $request) ? $request['years'] : [date("Y")];
        $request['name'] = array_key_exists('name', $request) ? $request['name'] : null;
        $request['product'] = array_key_exists('product', $request) ? $request['product'] : 'keluhan';

        $params = $this->validate(
            $request,
            [
                'years' => ['validators' => []],
                'name' => ['validators' => ['required']],
                'product' => ['validators' => [
                    'required',
                    [
                        'name' => 'inclusion_in',
                        'params' => ['diskusi', 'keluhan']
                    ]
                ]]
            ]
        );

        $monthsIn = [];
        foreach ($params['years'] as $year) {
            $monthsIn[$year] = array_fill(0, 12, 0);
        }

        $where = "d.type = 0 AND d.is_deleted = 0";
        $binds = [];

        // Set select column and group by based on product type
        $selectType = "trim(split_part(tdb.deskripsi, '-', 1))";
        $groupBy = "tdb.deskripsi";

        if (!empty($params['product']) && $params['product'] === 'diskusi') {
            $where .= ' AND p.id_index <> 9 AND p.is_active = TRUE';
            $selectType = "p.nama";
            $groupBy = "p.nama";
        }
        if (!empty($params['product']) && $params['product'] === 'keluhan') {
            $where .= " AND p.id_index = 9 AND p.is_active = TRUE AND tdb.type <> 'BANSOS' AND tdb.type <> 'COVID'";
        }

        if (!empty($params['name'])) {
            if ($params['product'] === 'diskusi') {
                $where .= " AND LOWER(p.nama) = :tipe";
            } else {
                $where .= " AND LOWER(trim(split_part(tdb.deskripsi, '-', 1))) = :tipe";
            }
            $binds['tipe'] = strtolower($params['name']);
        }

        if (!empty($params['years'])) {
            $years = '{' . implode(',', $params['years']) . '}';
            $where .= ' AND EXTRACT(YEAR FROM d.created_at) = ANY(:years)';
            $binds['years'] = $years;
        }

        $discussions = $this->rawQuery("
            SELECT
                diskusi.type,
                diskusi.year,
                diskusi.month,
                sum(diskusi.total) as total
            FROM (
                SELECT
                    $selectType as type,
                    TO_CHAR(d.created_at, 'YYYY') AS year,
                    TO_CHAR(d.created_at, 'MM') AS month,
                    COUNT(d.id) AS total
                FROM
                    jaga.produk p
                LEFT JOIN
                    jaga.kategori_diskusi kd ON kd.produk_id = p.id_index
                LEFT JOIN
                    jaga.diskusi d ON d.kategori_diskusi_id = kd.id
                LEFT JOIN
                    jaga.diskusi_bansos db ON db.id_diskusi = d.id
                LEFT JOIN
                    jaga.topik_diskusi_bansos tdb ON tdb.id = db.id_topik
                WHERE
                    $where
                GROUP BY
                    $groupBy, month, year
                ORDER BY
                    $groupBy, year, month
            ) diskusi
            GROUP BY
                diskusi.type,
                diskusi.year,
                diskusi.month
            ORDER BY
                diskusi.type,
                diskusi.year,
                diskusi.month
        ", $binds);

        if (count($discussions) > 0) {
            foreach ($discussions as $discussion) {
                if (intval($discussion['total']) > 0) {
                    $yearIndex = $discussion['year'];
                    $monthIndex = intval($discussion['month']) - 1;
                    $monthsIn[$yearIndex][$monthIndex] = intval($discussion['total']);
                }
            }
        }

        $result = [];
        foreach ($monthsIn as $year => $data) {
            $result[] = [
                'name' => $year,
                'data' => $data
            ];
        }

        return $result;
    }
}
