<?php

use LZCompressor\LZString;

class Bpjs2Client extends BackgroundBaseClient
{
    const FILTER_LAST_UPDATE = 30;

    public function filterResponse($body, $result) {
        //NOTE: Logger harus diset dari controller, karena didapat dari DI.
        parent::filterResponse($body, $result);
        if($this->logger != null) {
            if (!empty($result)) {
                if (array_key_exists('metaData', $result) && !empty($result['metaData']))
                    $this->logger->addArray('api_result_', $result['metaData']);
                elseif (array_key_exists('metadata', $result) && !empty($result['metadata']))
                    $this->logger->addArray('api_result_', $result['metadata']);
            }
        }
    }

    function getSignature($timestamp)
	{
		$secretKey = $this->prop["secret_key"];
		$consid = $this->prop["consid"];
		$hash = hash_hmac("sha256", $consid . "&" . $timestamp, $secretKey, true);
		$hashInBase64 = base64_encode($hash);

		return $hashInBase64;
	}

	function getTimestamp() {
	    $defaultTimezone = date_default_timezone_get();
	    date_default_timezone_set('UTC');
	    $result = strval(time() - strtotime('1970-01-01 00:00:00'));
	    if($defaultTimezone != 'UTC') date_default_timezone_set($defaultTimezone);
	    return $result;
    }

    function getHeader($timestamp) {
        return [
            "X-Cons-ID"   => $this->prop["consid"],
            "X-Timestamp" => $timestamp,
            "X-Signature" => $this->getSignature($timestamp),
            "user-key"    => $this->prop["user_key"]
        ];
    }

    function decryptResponse($timestamp, $response) {
        try {
            if ($this->isBpjsResponseSuccess($response)) {
                $encrypt_method = 'AES-256-CBC';

                $consid = getenv("BPJS2_CONS_ID");
                $conspwd = getenv("BPJS2_SECRET_KEY");

                $signature = $consid . $conspwd . $timestamp;

                // hash
                $key = hex2bin(hash('sha256', $signature));

                // iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
                $iv = substr(hex2bin(hash('sha256', $signature)), 0, 16);

                $output = openssl_decrypt(base64_decode($response['response']), $encrypt_method, $key, OPENSSL_RAW_DATA, $iv);
                $dOutput = LZString::decompressFromEncodedURIComponent($output);
                $response['response'] = json_decode($dOutput, true);
            }
        }
        catch (\Exception $e) {
            print_r($e->getMessage());
        }

        return $response;
    }

    public function getDetailByNIKAndTglLahir($nik, $tgl_lahir)
    {
        $timestamp = $this->getTimestamp();
		$method = "GET";
        $path = "/kpk-rest/nik/$nik/$tgl_lahir";
        $params = [
            "headers" => $this->getHeader($timestamp)
        ];

        //region LOG Definition
        $this->setApiName("bpjs2-get_detail_by_nik_and_tgl_lahir");
        $this->setLogParams([
            'nik' => $nik,
            'tgl_lahir' => $tgl_lahir,
        ]);
        //endregion

        $exec = $this->execute($method, $path, $params, false);
        return $this->decryptResponse($timestamp, $exec);
    }

    public function getReferensiProvinsi()
    {
        $timestamp = $this->getTimestamp();
        $method = "GET";
        $path = "/kpk-rest/referensi/propinsi";
        $params = [
            "headers" => $this->getHeader($timestamp)
        ];

        //region LOG Definition
        $this->setApiName("bpjs2-get_referensi_provinsi");;
        //endregion

        $exec = $this->execute($method, $path, $params, false);
        return $this->decryptResponse($timestamp, $exec);
    }

    public function getReferensiKabupaten($kodepropinsi)
    {
        $timestamp = $this->getTimestamp();
        $method = "GET";
        $path = "/kpk-rest/referensi/dati2/" . $kodepropinsi;
        $params = [
            "headers" => $this->getHeader($timestamp)
        ];

        //region LOG Definition
        $this->setApiName("bpjs2-get_referensi_kabupaten");
        $this->setLogParams([
            'kodepropinsi' => $kodepropinsi
        ]);
        //endregion

        $exec = $this->execute($method, $path, $params, false);
        return $this->decryptResponse($timestamp, $exec);
    }

    public function isBpjsResponseTimeout($response) {
        $result = false;
        if (!empty($response)) {
            if (array_key_exists('metaData', $response) && !empty($response['metaData'])) {
                if($response['metaData']['code'] == 412) {
                    $result = true;
                }
            }
        }

        return $result;
    }

    public function isBpjsResponseSuccess($response) {
        $result = false;
        if (!empty($response)) {
            if (array_key_exists('metaData', $response) && !empty($response['metaData'])) {
                if ($response['metaData']['code'] == '200') {
                    $result = true;
                }
            }
        }

        return $result;
    }

    public function updateAndGetProvinsiKabKota() {
        try {
            $totalExistingProvinsi = BpjsProvinsi::count();

            $totalExistingKabKota = BpjsKotaKabupaten::count();

            if ($totalExistingProvinsi == 0 || $totalExistingKabKota == 0) {
                $isGetPuskesmasTimeout = false;
                //TODO: set logger
                $respProvinsi = $this->getReferensiProvinsi();
                if (array_key_exists('metaData', $respProvinsi) && !$this->isBpjsResponseTimeout($respProvinsi)) {
                    if ($respProvinsi['metaData']['code'] == '200') {
                        $listProvinsi = $respProvinsi['response']['list'];
                        BpjsProvinsi::saveMultiple($listProvinsi);

                        foreach ($listProvinsi as $provinsi) {
                            if ($isGetPuskesmasTimeout) break;
                            $respKabupaten = $this->getReferensiKabupaten($provinsi['kodepropinsi']);
                            if ($this->isBpjsResponseTimeout($respKabupaten)) break;

                            if (array_key_exists('metaData', $respKabupaten)) {
                                if ($respKabupaten['metaData']['code'] == '200') {
                                    $listKabupaten = $respKabupaten['response']['list'];

                                    BpjsKotaKabupaten::saveMultiple($listKabupaten, $provinsi);
                                }
                            }
                        }
                    }
                }
            }
            $dati2Data = BpjsKotaKabupaten::find([
                'conditions' => 'puskesmas_last_update_at IS NULL OR puskesmas_last_update_at <= ?0 OR puskesmas_last_update_page <> ?1 OR puskesmas_last_update_page IS NULL',
                'bind' => [
                    date('Y-m-d', strtotime('-' . self::FILTER_LAST_UPDATE . ' day')),
                    'LAST'
                ]
            ]);

            return $dati2Data;
        }
        catch (Exception $e) {
            \Sentry\captureException($e);
            print_r($e->getMessage());
        }
    }

    public function getProfilPuskesmas($pagenumber, $kodedati2)
    {
        $timestamp = $this->getTimestamp();

        $method = "GET";
        $path = "/kpk-rest/puskesmas/list/" . $pagenumber .'/' . $kodedati2;
        $params = [
            "headers" => $this->getHeader($timestamp)
        ];

        //region LOG Definition
        $this->setApiName("bpjs2-get_profil_puskesmas");
        $this->setLogParams([
            'pagenumber' => $pagenumber,
            'kodedati2' => $kodedati2,
        ]);
        //endregion

        $exec = $this->execute($method, $path, $params, false);
        return $this->decryptResponse($timestamp, $exec);
    }

    public function getKapitasi($kodePuskesmas)
    {
        $timestamp = $this->getTimestamp();

        $method = "GET";
        $path = "/kpk-rest/kapitasi/data/" . $kodePuskesmas;
        $params = [
            "headers" => $this->getHeader($timestamp)
        ];

        //region LOG Definition
        $this->setApiName("bpjs2-get_dana_kapitasi");
        $this->setLogParams([
            'kodepuskesmas' => $kodePuskesmas
        ]);
        //endregion

        $exec = $this->execute($method, $path, $params, false);
        return $this->decryptResponse($timestamp, $exec);
    }
}
