<?php
class Satu_sehat
{
  var $base_url, $base_consent_url, $base_oauth_url;
  var $is_staging, $organizationID;
  var $key, $secret;
  var $tz;
  var $db, $dbname;
  function __construct()
  {
    $this->tz = "+07:00";
    $this->is_staging = false;
    $this->base_url = "https://api-satusehat.kemkes.go.id/fhir-r4/v1";
    $this->base_oauth_url = "https://api-satusehat.kemkes.go.id/oauth2/v1";
    $this->base_consent_url = "https://api-satusehat.dto.kemkes.go.id/consent/v1";
    $CI = &get_instance();
    $this->db = $CI->load->database("default", true);

    $this->dbname = "one_health";
    if ($this->is_staging) {
      $this->base_url = "https://api-satusehat-stg.kemkes.go.id/fhir-r4/v1";
      $this->base_oauth_url = "https://api-satusehat-stg.kemkes.go.id/oauth2/v1";
      $this->base_consent_url = "https://api-satusehat-stg.dto.kemkes.go.id/consent/v1";
      $this->dbname = "one_health_dev";
    }
    $this->get_organization_id();
  }
  function load_clinic()
  {
    $this->dbname = "one_health_clinic";
  }
  function ss_organization()
  {
    $this->get_organization_id();
    $o_resp = $this->ss_get("/Organization/{$this->organizationID}");
    $resp = $this->objToArray($o_resp);
    $id = $resp["id"];
    $name = $resp["name"];
    $x_type = $resp["type"][0]["coding"][0];
    $type = $x_type["display"];
    $code = $x_type["code"];
    $system = $x_type["system"];
    return json_encode([
      "ID" => $id,
      "Name" => $name,
      "Type" => $type,
      "CodeSystem" => $code . " | " . $system,
    ]);
  }

  function search_practicioner_by_nik($nik, $debug = "")
  {
    $service =  "/Practitioner?identifier=https://fhir.kemkes.go.id/id/nik|" . $nik;
    $o_resp = $this->ss_get($service, $debug);
    $resp = $this->objToArray($o_resp);
    if (count($resp["entry"]) > 0) {
      $rs = $resp["entry"][0]["resource"];
      return json_encode([
        "status" => "OK",
        "ihsID" => $rs["id"],
        "name" =>  $rs["name"][0]["text"]
      ]);
    }
    return json_encode([
      "status" => "ERR",
      "message" => "Practitioner not found [$nik]"
    ]);
  }

  function search_patient_by_nik($nik, $debug = "")
  {
    $service = "/Patient?identifier=https://fhir.kemkes.go.id/id/nik|" . $nik;
    $resp = $this->ss_get($service);
    if ($debug != "") {
      echo "resp : ";
      print_r($resp);
    }
    $a_resp = $this->objToArray($resp);
    if (isset($a_resp["entry"][0]["resource"]["id"])) {
      return $a_resp["entry"][0]["resource"]["id"];
    }
    return "";
  }

  function location_by_organization($organizationIhsID, $debug = "")
  {
    $service = "/Location?organization=" . $organizationIhsID;
    $resp = $this->ss_get($service);
    if ($debug != "") {
      echo "resp : ";
      print_r($resp);
    }
    $a_resp = $this->objToArray($resp);
    return $a_resp;
  }

  function location_create(
    $code,
    $name,
    $description,
    $address,
    $city,
    $kodePos,
    $administrativeCode,
    $rt,
    $rw,
    $phone,
    $type,
    $partOf = "",
    $email = "",
    $fax = "",
    $url = "",
    $long = "",
    $lat = ""
  ) {
    $this->get_organization_id();
    $organizationID = $this->organizationID;
    list($type_code, $type_display) = explode("^", $type);
    $telecom = [];
    $telecom[] = ["system" => "phone", "value" => "$phone", "use" => "work"];
    if ($fax != "")   $telecom[] = ["system" => "fax", "value" => "$fax", "use" => "work"];
    if ($email != "") $telecom[] = ["system" => "email", "value" => "$email"];
    if ($url != "")   $telecom[] = ["system" => "url", "value" => "$url"];
    $provCode = substr($administrativeCode, 0, 2);
    $cityCode = substr($administrativeCode, 0, 4);
    $districtCode = substr($administrativeCode, 0, 7);
    $villageCode = substr($administrativeCode, 0, 10);

    $data = [
      "resourceType" => "Location",
      "identifier" => [
        [
          "system" => "http://sys-ids.kemkes.go.id/location/{$organizationID}",
          "value" => "$code",
        ],
      ],
      "status" => "active",
      "name" => "$name",
      "description" => "$description",
      "mode" => "instance",
      "telecom" => $telecom,
      "address" => [
        "use" => "work",
        "line" => [
          $address,
        ],
        "city" => "$city",
        "postalCode" => "$kodePos",
        "country" => "ID",
        "extension" => [
          [
            "url" =>
            "https://fhir.kemkes.go.id/r4/StructureDefinition/administrativeCode",
            "extension" => [
              ["url" => "province", "valueCode" => $provCode],
              ["url" => "city", "valueCode" => $cityCode],
              ["url" => "district", "valueCode" => "$districtCode"],
              ["url" => "village", "valueCode" => "$villageCode"],
              ["url" => "rt", "valueCode" => "$rt"],
              ["url" => "rw", "valueCode" => "$rw"],
            ],
          ],
        ],
      ],
      "physicalType" => [
        "coding" => [
          [
            "system" =>
            "http://terminology.hl7.org/CodeSystem/location-physical-type",
            "code" => "$type_code",
            "display" => "$type_display",
          ],
        ],
      ],
      "position" => [
        "longitude" => intval($long),
        "latitude" => intval($lat),
        "altitude" => 0,
      ],
      "managingOrganization" => ["reference" => "Organization/{$organizationID}"],
    ];
    if ($partOf != "") {
      $data["partOf"] = [
        "reference" => "Location/$partOf"
      ];
    }
    $service = "/Location";
    $resp = $this->ss_post($service, $data);
    $oresp = $this->objToArray($resp);
    if (!isset($oresp["id"])) {
      header("Content-Type: text/plain");
      print_r($data);
      print_r($resp);
    }
    return $oresp;
  }

  function location_nonactive(
    $ihsID
  ) {
    $this->get_organization_id();
    $data = [
      [
        "op" => "replace",
        "path" => "/status",
        "value" => "inactive"
      ]
    ];

    $service = "/Location/$ihsID";
    $resp = $this->ss_patch($service, $data);
    return $this->objToArray($resp);
  }
  function get_location($locationID)
  {
  }




  function encounter_by_id($encounterID)
  {
    $this->get_organization_id();
    $service = "/Encounter/$";
    $resp = $this->ss_get($service);
    return $this->objToArray($resp);
  }
  function encounter_by_subject($patientIhsID)
  {
    $this->get_organization_id();
    $service = "/Encounter?subject=$patientIhsID";
    $resp = $this->ss_get($service);
    return $this->objToArray($resp);
  }
  function encounter(
    $orderDate,
    $patientIhsID,
    $patientName,
    $doctorIhsID,
    $doctorName,
    $locationID,
    $locationName,
    $labNumber,
    $tz = "+07:00",
    $payload_only = false
  ) {
    $service = "/Encounter";
    $xdate = substr($orderDate, 0, 10) . "T" . substr($orderDate, 11) . $tz;
    $this->get_organization_id();
    $param = $this->encounter_param(
      $patientIhsID,
      $patientName,
      $doctorIhsID,
      $doctorName,
      $locationID,
      $locationName,
      $this->organizationID,
      $labNumber,
      $xdate
    );
    $payload = json_encode($param);
    if ($payload_only) {
      return ["", "", $payload, ""];
    }
    $jresp = $this->ss_post($service, $param);
    $resp = $this->objToArray($jresp);
    $response = json_encode($resp);
    if (is_array($resp) && isset($resp["id"])) {
      $encounterResponseID = $resp["id"];
      return [$encounterResponseID, "", $payload, $response];
    } else {
      return ["", $response, $payload, $response];
    }
  }
  function encounter_param(
    $patientIhs,
    $patientName,
    $dpjpIhs,
    $dpjpName,
    $locationIhs,
    $locationName,
    $organizationID,
    $orderHeaderNumber,
    $dateTime // 2022-06-14T07:00:00+07:00
  ) {
    if ($this->is_staging) {
      $dpjpIhs = "N10000001";
      $dpjpName = "Dokter Bronsig";
    }
    $encounterParam = [
      "resourceType" => "Encounter",
      "status" => "arrived",
      "class" => [
        "system" => "http://terminology.hl7.org/CodeSystem/v3-ActCode",
        "code" => "AMB",
        "display" => "ambulatory"
      ],
      "subject" => [
        "reference" => "Patient/{$patientIhs}",
        "display" => "$patientName"
      ],
      "participant" => [
        [
          "type" => [
            [
              "coding" => [
                [
                  "system" => "http://terminology.hl7.org/CodeSystem/v3-ParticipationType",
                  "code" => "ATND",
                  "display" => "attender"
                ]
              ]
            ]
          ],
          "individual" => [
            "reference" => "Practitioner/{$dpjpIhs}",
            "display" => "$dpjpName"
          ]
        ]
      ],
      "period" => [
        "start" => "$dateTime",
      ],
      "location" => [
        [
          "location" => [
            "reference" => "Location/$locationIhs",
            "display" => "$locationName"
          ]
        ]
      ],
      "statusHistory" => [
        [
          "status" => "arrived",
          "period" => [
            "start" => $dateTime
          ]
        ]
      ],
      "serviceProvider" => [
        "reference" => "Organization/{$organizationID}"
      ],
      "identifier" => [
        [
          "system" => "http://sys-ids.kemkes.go.id/encounter/{$organizationID}",
          "value" => "$orderHeaderNumber"
        ]
      ]
    ];
    return $encounterParam;
  }




  // helper
  function get_organization_id()
  {

    $sql = "SELECT organizationID
      FROM {$this->dbname}.organization
      JOIN m_branch ON organizationM_BranchID = M_BranchID AND M_BranchIsDefault = 'Y' AND M_BranchIsActive = 'Y'
      WHERE organizationIsActive = 'Y'";
    $qry = $this->db->query($sql);
    if (!$qry) {
      return;
    }
    $rows = $qry->result_array();
    if (count($rows) > 0) {
      $this->organizationID = $rows[0]["organizationID"];
    }
  }

  function ss_patch($service, $data)
  {
    $token = $this->get_token();
    $authorization = "Authorization: Bearer " . $token;
    $xbase_url = $this->base_url;
    $url = $xbase_url . "$service";
    $ch = curl_init($url);
    # Setup request to send json via POST.
    $payload = json_encode($data);

    curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json-patch+json', $authorization));
    # Return response instead of printing.
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    # Send request.
    $result = curl_exec($ch);
    curl_close($ch);
    # Print response.
    $data_rst = json_decode($result);
    return $data_rst;
  }

  function get_client_key($debug = "")
  {
    $sql = "select * from {$this->dbname}.client where clientIsActive = 'Y'";
    $qry = $this->db->query($sql);
    if (!$qry) {
      return [false, "", ""];
    }
    $rows = $qry->result_array();
    if (count($rows) == 0) {
      if ($debug != "") {
        print_r([false, "", ""]);
      }
      return [false, "", ""];
    }
    if ($debug != "") {
      print_r([true, $rows[0]["clientKey"], $rows[0]["clientSecret"]]);
    }

    return [true, $rows[0]["clientKey"], $rows[0]["clientSecret"]];
  }

  function reset_token()
  {
    $sql = "delete from {$this->dbname}.token ";
    $qry = $this->db->query($sql);
    if (!$qry) {
      echo "ERR : " . $this->db->error()["message"];
      echo "      " . $this->db->last_query();
      exit;
    }
  }

  function put_token()
  {
    $auth_url = $this->base_oauth_url;
    $url = $auth_url . "/accesstoken?grant_type=client_credentials";
    list($status, $key, $secret) = $this->get_client_key();
    $data = [
      "client_id" => $key,
      "client_secret" => $secret
    ];
    $ch = curl_init($url);
    $post_data = http_build_query($data);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $result = curl_exec($ch);
    curl_close($ch);
    if ($result) {
      $token_rst = json_decode($result);
      $sql = "select count(*) as xcount, tokenID
                from {$this->dbname}.token
                where
                tokenIsActive = 'y'
                ";
      $qry = $this->db->query($sql);
      if (!$qry) {
        echo "get count token error";
        exit;
      }

      $rst_count = $qry->row_array();
      // print_r($token_rst);
      if ($rst_count['xcount'] > 0) {
        $sql = "update {$this->dbname}.token set tokenValue = ?, tokenExpired = date_add(now(), interval 50 minute)
                    where tokenID = ?";
        $qry = $this->db->query($sql, [$token_rst->access_token, $rst_count['tokenID']]);
        if (!$qry) {
          $this->sys_error_db("refresh token error", $this->db->last_query());
          exit;
        }
      } else {
        $sql = "update {$this->dbname}.token set tokenIsActive = 'N' where tokenIsActive = 'Y'";
        $qry = $this->db->query($sql);
        if (!$qry) {
          echo "nonactive token error";
          exit;
        }

        $sql = "insert into {$this->dbname}.token(tokenValue,tokenExpired) values(?,date_add(now(), interval 50 minute))";
        $qry = $this->db->query($sql, [$token_rst->access_token]);
        if (!$qry) {
          echo "insert token error";
          exit;
        }
      }

      $sql = "select tokenValue
                from {$this->dbname}.token
                where
                tokenIsActive = 'Y' limit 1
                ";
      $qry = $this->db->query($sql);
      if (!$qry) {
        echo "get token error";
        exit;
      }

      return $qry->row()->tokenValue;
    }
  }

  function get_token()
  {
    $sql = "SELECT COUNT(*) as xcount, tokenValue
            FROM {$this->dbname}.token 
            WHERE tokenIsActive = 'Y' AND NOW() < tokenExpired AND tokenValue IS NOT NULL ";
    $qry = $this->db->query($sql);
    $this->check_error($qry, "select token");

    $data_token = $qry->row_array();
    //print_r($data_token);
    if ($data_token['xcount'] > 0) {
      return $data_token['tokenValue'];
    } else {
      return $this->put_token();
    }
  }


  function ss_post($service, $data)
  {
    $token = $this->get_token();
    $authorization = "Authorization: Bearer " . $token;
    $xbase_url = $this->base_url;
    $url = $xbase_url . "$service";
    $ch = curl_init($url);
    # Setup request to send json via POST.
    $payload = json_encode($data);

    curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', $authorization));
    # Return response instead of printing.
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    # Send request.
    $result = curl_exec($ch);
    curl_close($ch);
    # Print response.
    $data_rst = json_decode($result);
    return $data_rst;
  }
  function ss_get($service, $debug = "")
  {
    $token = $this->get_token();
    $authorization = "Authorization: Bearer " . $token;
    $xbase_url = $this->base_url;
    $url = $xbase_url . "$service";
    $ch = curl_init($url);

    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', $authorization));
    # Return response instead of printing.
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    # Send request.
    $result = curl_exec($ch);
    curl_close($ch);
    # Print response.
    if ($debug != "") {
      echo "url : $url \n";
      print_r($result);
    }
    $data_rst = json_decode($result);
    return $data_rst;
  }
  protected function objToArray($obj)
  {
    if (!is_object($obj) && !is_array($obj)) {
      return $obj;
    }
    foreach ($obj as $key => $value) {
      $arr[$key] = $this->objToArray($value);
    }
    return $arr;
  }

  function check_error($qry, $stage)
  {
    if (!$qry) {
      echo json_encode([
        "status" => "ERR",
        "message" => $this->db->error(),
        "sql" => $this->db->last_query()
      ]);
      exit;
    }
  }
}
