<?php

class PerguruanTinggiService {
    private $context;
    private $pt;

    #region PUBLIC
    public function __construct($context) {
        $this->context = $context;
    }

    /**
     * @throws CustomErrorException
     * @throws Exception
     */
    public function detail($idPt) {
        $this->pt = PerguruanTinggi::findFirst(
            [
                "id_pt = :id: and deleted_at is null",
                "bind" => [
                    "id" => $idPt
                ]
            ]
        );
        if (empty($this->pt)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Perguruan Tinggi tidak ditemukan');
        }
        $this->syncDetail();
        return $this->pt;
    }

    public function ptnbh($idPt) {
        $result = [];
        $ptnbh = ListPTNBH::findFirst(
            [
                "perguruan_tinggi_id = :pt_id: and deleted_at is null",
                "bind" => [
                    "pt_id" => $idPt
                ]
            ]
        );
        if (!empty($ptnbh)) {
            $data = PendapatanBelanjaPTNBH::query()
                ->columns('PendapatanBelanjaPTNBH.tahun, InfoAlokasiPendapatanBelanja.tipe_alokasi, PendapatanBelanjaPTNBH.nilai_alokasi')
                ->join('InfoAlokasiPendapatanBelanja', 'InfoAlokasiPendapatanBelanja.id = PendapatanBelanjaPTNBH.info_alokasi_pendapatan_belanja_id')
                ->where('PendapatanBelanjaPTNBH.perguruan_tinggi_id = :id_pt: and InfoAlokasiPendapatanBelanja.id NOT IN (SELECT x.parent_id FROM InfoAlokasiPendapatanBelanja x) and PendapatanBelanjaPTNBH.deleted_at is null')
                ->bind(["id_pt" => $idPt])
                ->execute()
                ->toArray();

            $processedData = [];
            foreach ($data as $content) {
                if (!array_key_exists($content['tahun'], $processedData)) {
                    $processedData[$content['tahun']]['pendapatan'] = $processedData[$content['tahun']]['belanja'] = 0;
                }

                if ($content['tipe_alokasi'] == 'Pendapatan') {
                    $processedData[$content['tahun']]['pendapatan'] += $content['nilai_alokasi'];
                } else {
                    $processedData[$content['tahun']]['belanja'] += $content['nilai_alokasi'];
                }
            }

            foreach ($processedData as $tahun => $data) {
                $detail['tahun'] = $tahun;
                $detail['pendapatan'] = $data['pendapatan'];
                $detail['belanja'] = $data['belanja'];
                $detail['status'] = $data['pendapatan'] > $data['belanja'] ? 'Surplus' : 'Defisit';
                $result[] = $detail;
            }
        }
        return $result;
    }

    public function anggaran($idPt) {
        if (empty($this->pt)) {
            $pt = PerguruanTinggi::findFirst(
                [
                    "id_pt = :id: and deleted_at is null",
                    "bind" => [
                        "id" => $idPt
                    ]
                ]
            );

            if (empty($pt)) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Perguruan Tinggi tidak ditemukan');
            }
        } else {
            $pt = $this->pt;
        }

        //Get Perguruan Tinggi Type
        $ptnbh = ListPTNBH::findFirst(
            [
                "perguruan_tinggi_id = :pt_id: and deleted_at is null",
                "bind" => [
                    "pt_id" => $pt->id_pt
                ]
            ]
        );

        if (!empty($ptnbh)) {
            $result['tipe_pt'] = 'PTNBH';
        } else {
            $result['tipe_pt'] = 'PTN';
        }

        //Get Alokasi BOPTN/BPPTNBH
        $result['summary_alokasi'] = [];

        $listAlokasi = AlokasiBOPTNBPPTNBH::find(
            [
                "perguruan_tinggi_id = :pt_id: and deleted_at is null",
                "bind" => [
                    "pt_id" => $pt->id_pt
                ]
            ]
        );

        foreach ($listAlokasi as $alokasi) {
            $data = $alokasi->toArray();
            $data['detail_alokasi'] = DetailAlokasiBOPTN::query()
                ->columns("InfoAlokasiBOPTN.kode_output, InfoAlokasiBOPTN.nama_output, DetailAlokasiBOPTN.nilai_alokasi")
                ->join("AlokasiBOPTNBPPTNBH", "AlokasiBOPTNBPPTNBH.id = DetailAlokasiBOPTN.alokasi_id")
                ->join("InfoAlokasiBOPTN", "InfoAlokasiBOPTN.id = DetailAlokasiBOPTN.info_alokasi_boptn_id")
                ->where("AlokasiBOPTNBPPTNBH.id = :alokasi_id: and AlokasiBOPTNBPPTNBH.tahun = :tahun: and DetailAlokasiBOPTN.deleted_at is null")
                ->bind(["alokasi_id" => $alokasi->id, "tahun" => $alokasi->tahun])
                ->execute()
                ->toArray();


            //Get Jumlah Anggaran tiap Mahasiswa untuk PTNBH
            $data['anggaran_per_mahasiswa'] = 0;
            if (!empty($ptnbh)) {
                $rawJumlahMahasiswa = JumlahDosenMahasiswaPt::query()
                    ->columns("SUM(jumlah_mahasiswa) as jumlah_mahasiswa")
                    ->where("perguruan_tinggi_id = :id_pt: and deleted_at is null")
                    ->bind(["id_pt" => $pt->id_pt])
                    ->execute()
                    ->toArray()[0];

                $jumlahMahasiswa = $rawJumlahMahasiswa['jumlah_mahasiswa'] ?: 0;

                if ($jumlahMahasiswa > 0) {
                    $data['anggaran_per_mahasiswa'] = $data['total_alokasi'] / $jumlahMahasiswa;
                }
            }
            $result['summary_alokasi'][] = $data;
        }

        return $result;
    }

    public function pendapatanPercentage($idPt, $tahun, $kodeOutput = "PSC005") {
        $infoPendapatanBelanja = InfoAlokasiPendapatanBelanja::findFirstByKodeOutput($kodeOutput);
        if (empty($infoPendapatanBelanja)) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Kode Tidak Valid');
        }

        $rawData = PendapatanBelanjaPTNBH::query()
            ->columns('PendapatanBelanjaPTNBH.nilai_alokasi as nilai_alokasi, (SELECT SUM(x.nilai_alokasi) FROM PendapatanBelanjaPTNBH x WHERE x.info_alokasi_pendapatan_belanja_id IN (SELECT y.id FROM InfoAlokasiPendapatanBelanja y WHERE y.tipe_alokasi = :tipe_alokasi:) and x.tahun = :tahun: and x.perguruan_tinggi_id = :id_pt:) as total_alokasi')
            ->join('InfoAlokasiPendapatanBelanja', 'InfoAlokasiPendapatanBelanja.id = PendapatanBelanjaPTNBH.info_alokasi_pendapatan_belanja_id')
            ->where('PendapatanBelanjaPTNBH.info_alokasi_pendapatan_belanja_id = :info_id: and PendapatanBelanjaPTNBH.tahun = :tahun: and PendapatanBelanjaPTNBH.perguruan_tinggi_id = :id_pt:')
            ->bind(
                [
                    "tipe_alokasi" => $infoPendapatanBelanja->tipe_alokasi,
                    "tahun" => $tahun,
                    "info_id" => $infoPendapatanBelanja->id,
                    "id_pt" => $idPt
                ]
            )
            ->execute()
            ->toArray();

        $result = [];
        if (count($rawData) > 0) {
            $data = $rawData[0];

            $result['percentage'] = $data['nilai_alokasi'] / $data['total_alokasi'];
            $result['info'] = $infoPendapatanBelanja->toArray();
        }

        return $result;
    }

    public function prodi($idPt) {
        $strWhere = "perguruan_tinggi_id = :id: and deleted_at is null";
        $bind['id'] = $idPt;
        return DetailProdiPt::find(
            [
                $strWhere,
                "bind" => $bind
            ]
        );
    }

    /**
     * @throws CustomErrorException
     * @throws Exception
     */
    public function prodiSummary($idPt) {
        if (empty($this->pt)) {
            $pt = PerguruanTinggi::findFirst(
                [
                    "id_pt = :id: and deleted_at is null",
                    "bind" => [
                        "id" => $idPt
                    ]
                ]
            );

            if (empty($pt)) {
                throw new CustomErrorException(BaseResponse::INVALID_REQUEST, 'Perguruan Tinggi tidak diketemukan');
            }
        } else {
            $pt = $this->pt;
        }

        $this->syncProdi($pt);

        return DetailProdiPt::query()
            ->columns('jenjang as jenjang, count(1) as jumlah_prodi')
            ->where('perguruan_tinggi_id = :id: and deleted_at is null')
            ->bind(['id' => $pt->id_pt])
            ->groupBy('jenjang')
            ->execute()
            ->toArray();
    }
    #endregion

    #region PRIVATE
    /**
     * @throws Exception
     */
    private function syncDetail() {
        //Get Live Data
        $kemdikbudClient = new KemdikbudClient($this->context->config["pt_prop"]);
        $kemdikbudClient->setLogger($this->context->logger);
        $result = [];
        $resultAkreditasi = [];

        try {
            $result = $kemdikbudClient->getPtbyName(str_replace(' ', '+', $this->pt->nama), true);
        } catch (\Exception $ex) {
            \Sentry\captureException($ex);
        }

        if (count($result) > 0) {
            //Update Local Data
            $liveData = $result[0];

            $pt = $this->pt;
            $pt->kode = $liveData['kode'] ?: '';
            $pt->nama_singkat = $liveData['nama_singkat'] ?: '';
            $pt->keterangan = $liveData['kode_satker'] ?: '';
            $pt->tgl_sk_pendirian = $liveData['tgl_sk_pendirian'] ?: '';
            $pt->status = $liveData['status'] ?: '';
            $pt->telepon = $liveData['telepon'] ?: '';
            $pt->faksimile = $liveData['faksimile'] ?: '';
            $pt->website = $liveData['website'] ?: '';
            $pt->email = $liveData['email'] ?: '';
            $pt->bentuk_pendidikan = $liveData['bentuk_pendidikan']['nama'] ?: '';
            $pt->last_update_dikti = $liveData['last_update'] ?: '';
            $pt->kota_kab = $liveData['alamat']['kab_kota']['nama'] ?: '';
            $pt->provinsi = $liveData['propinsi']['nama'] ?: '';
            $pt->updated_at = 'now()';

            try {
                $resultAkreditasi = $kemdikbudClient->getAkreditasibyPT($pt->kode);
            } catch (\Exception $ex) {

            }

            if (count($resultAkreditasi) > 0) {
                $pt->status = $resultAkreditasi[0]['nilai'] ?: '';
            }

            $pt->save();

            //region Update Meilisearch Data
            if(filter_var(getenv("MEILI_SEARCH_ACTIVE"), FILTER_VALIDATE_BOOLEAN)) {
                try {
                    $meilisearch = new MeiliSearchService($this->context);
                    $itemMeili = [
                        "id" => "perguruan-tinggi-" . $pt->id_pt,
                        "sector" => $meilisearch::SECTOR_PELAYANAN_PUBLIK,
                        "category" => $meilisearch::CATEGORY_PERGURUAN_TINGGI,
                        "substance" => $meilisearch::SUBSTANCE_DETAIL_PT,
                        "title" => $pt->nama,
                        "content" => $pt->nama . " " . $pt->nama_singkat . ", Provinsi " . $pt->provinsi . " Kota/Kabupaten " . $pt->kota_kab,
                        "keywords" => "Nama " . $pt->nama . " Nama Singkat " . $pt->nama_singkat . "  keterangan" . $pt->keterangan . " status badan hukum badanhukum " . $pt->status_badanhukum . " akreditasi " . $pt->status . " no telepon tlp " . $pt->telepon . " bentuk pendidikan " . $pt->bentuk_pendidikan,
                        "FEURL" => "/pelayanan-publik/universitas/" . $pt->id_pt . "/" . $pt->kode . "/" . $pt->nama
                    ];
                    $meilisearch->updateSingleDocument($itemMeili);
                } catch (\Exception $e) {}
            }
            //endregion

            $pt = PerguruanTinggi::findFirst(
                [
                    "id_pt = :id: and deleted_at is null",
                    "bind" => [
                        "id" => $pt->id_pt
                    ]
                ]
            );
            $this->pt = $pt;
        }
    }

    private function syncProdi($pt) {
        //Get Live Data
        $kemdikbudClient = new KemdikbudClient($this->context->config["pt_prop"]);
        $kemdikbudClient->setLogger($this->context->logger);
        $result = [];

        try {
            $result = $kemdikbudClient->getProdibyPT($pt->kode, true);
        } catch (\Exception $ex) {

        }

        if (count($result) > 0) {
            foreach ($result as $prodi) {
                $currentProdi = DetailProdiPt::findFirst(
                    [
                        "perguruan_tinggi_id = :id: and jenjang ilike :jenjang: and kode_prodi = :kode_prodi: and deleted_at is null",
                        "bind" => [
                            "id" => $pt->id_pt,
                            "jenjang" => $prodi['jenjang_didik']['nama'],
                            "kode_prodi" => $prodi['kode']
                        ]
                    ]
                );

                if (!empty($currentProdi)) {
                    $currentProdi->nama_prodi = $prodi['nama'] ?: '';
                    $currentProdi->akreditasi = $prodi['status'] ?: '';
                    $currentProdi->sks_lulus = $prodi['sks_lulus'] ?: 0;
                    $currentProdi->updated_at = 'now()';
                    $currentProdi->save();
                } else {
                    $newProdi = new DetailProdiPt();
                    $newProdi->perguruan_tinggi_id = $pt->id_pt;
                    $newProdi->jenjang = $prodi['jenjang_didik']['nama'] ?: '';
                    $newProdi->kode_prodi = $prodi['kode'] ?: '';
                    $newProdi->nama_prodi = $prodi['nama'] ?: '';
                    $newProdi->akreditasi = $prodi['status'] ?: '';
                    $newProdi->sks_lulus = $prodi['sks_lulus'] ?: 0;
                    $newProdi->create();
                }
            }
        }
    }
    #endregion
}
