<?php
header("Access-Control-Allow-Origin: *");
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header("Access-Control-Allow-Headers: X-API-KEY, Origin, X-Requested-With, Authorization,  client-security-token, Content-Type, Accept, Access-Control-Request-Method");
defined('BASEPATH') or exit('Sorry request could not be completed');
require APPPATH . 'libraries/REST_Controller.php';
require_once  APPPATH . 'libraries/jwt/JWT.php';
require_once  APPPATH . 'libraries/jwt/BeforeValidException.php';
require_once  APPPATH . 'libraries/jwt/ExpiredException.php';
require_once  APPPATH . 'libraries/jwt/SignatureInvalidException.php';

use \Firebase\JWT\JWT;

class Api extends CI_Controller
{

    public function __construct()
    {
        parent::__construct();
        $this->load->model('auditmodel');
        $this->load->model('msgmodel');
        $this->load->model('apimodel');
        $this->load->model('vouchersmodel');
        $this->load->model('tokenmodel');
        $this->load->model('notificationsmodel');
        $this->load->model('floatmodel');
        $this->load->library('form_validation');
        $this->load->helper('string');

        $method =  $this->input->server('REQUEST_METHOD');
        if ($method == "OPTIONS") {
            die();
        }
    }

    function getAuthorizationHeader()
    {
        $headers = null;
        if (isset($_SERVER['Authorization'])) {
            $headers = trim($_SERVER["Authorization"]);
        } else if (isset($_SERVER['HTTP_AUTHORIZATION'])) { //Nginx or fast CGI
            $headers = trim($_SERVER["HTTP_AUTHORIZATION"]);
        } elseif (function_exists('apache_request_headers')) {
            $requestHeaders = apache_request_headers();
            $requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders));
            if (isset($requestHeaders['Authorization'])) {
                $headers = trim($requestHeaders['Authorization']);
            }
        }
        return $headers;
    }

    function getBearerToken()
    {
        $headers = $this->getAuthorizationHeader();

        // HEADER: Get the access token from the header
        if (!empty($headers)) {

            if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
                return $matches[1];
            }
        }
        return null;
    }

    public function response($data = array(), $http_code = null)
    {
        if (empty($data) && $http_code === null) {
            $http_code = 404;
        } else {
            is_numeric($http_code) or $http_code = 200;
            header('Content-type: application/json');
            $output = json_encode($data);
        }
        header('HTTP/1.1: ' . $http_code);
        header('Status: ' . $http_code);
        exit($output);
    }

    private function checkToken()
    {
        $mtoken = array();
        $autho = $this->getBearerToken();
        if (!empty($autho)) {
            try {
                $decoded = JWT::decode($this->getBearerToken(), KEY, array('HS256'));
                $decoded_array = (array) $decoded;
                if ($decoded_array['exp'] >  strtotime('Y-m-d H:i:s')) {
                    $mtoken  = $decoded_array;
                }
            } catch (Exception $e) {
            }
        }
        return $mtoken;
    }

    public function Business($action = "")
    {
        if (strtolower($action) == "getusertoken") {
            $this->login();
        } elseif (strtolower($action) == "verifytransaction") {
            $this->verify();
        } else if (strtolower($action) == "utilizedvouher" || strtolower($action) == "utilizedvouher") {
            $this->utilized();
        } else {
            $this->unauthorized("Invalid option");
        }
    }

    private function unauthorized($message, $status = 200)
    {
        $this->response(array("resultDesc" => "Error", "resultCode" => "2", "message" => $message), $status);
    }

    private function login()
    {
        $post = $this->input->get();
        $postData = json_encode($post);
        $username =  $this->input->get('username');
        $password =  $this->input->get('password');

        if (empty($username) || empty($password)) {
            $this->auditmodel->insert("Naivas api tried to generate token and failed, username or password not provided", 0, "NAIVAS", json_encode($post));
            $this->unauthorized('Data validation failed : Username or password not provided');
        } else {

            $result = $this->apimodel->get(array('client_id' => $username, 'client_secret' => $password, 'client' => 'NAIVAS', 'record_state' => 0));
            if ($result->num_rows() > 0) {
                $row = $result->row();

                $token = do_hash(uniqid($row->client . '$$@#FRW$%YHRTEW748@fh!()_+ETERT^%$^&*0dhaa!`' . date('Y-m-d H:i:s'), true));
                $this->tokenmodel->insert($token, $row->client);

                $mtoken = array();
                $mtoken['token'] = $token;
                $mtoken['merchantCode'] = $row->merchantcode;
                $mtoken['unitCode'] = $row->unitcode;
                $mtoken['merchantType'] = $row->type;
                $mtoken['iat'] = strtotime(date('Y-m-d H:i:s'));
                $mtoken['exp'] = strtotime('+' . APP_TIMEOUT . ' hours');
                $output_data['yourToken'] = JWT::encode($mtoken, KEY);
                $output_data['tokenType'] = 'Bearer';
                $output_data['expiryHours'] = APP_TIMEOUT;
                $output_data['merchantCode'] = $row->merchantcode;
                $output_data['unitCode'] = $row->unitcode;
                $output_data['merchantType'] = $row->type;

                $out = array(
                    "resultDesc" => "Success",
                    "resultCode" => "0",
                    "message" => $output_data
                );

                $this->auditmodel->insert("Naivas api user Logged into the sytem ", 1, "NAIVAS", json_encode($out));
                $this->response($out, 200);
            } else {
                $this->auditmodel->insert("Naivas api tried to generate token and failed, invalid credentials", 0, "NAIVAS", json_encode($post));
                $this->unauthorized('Invalid Credentials');
            }
        }
    }

    public function verify()
    {
        $token = $this->checkToken();
        //$this->auditmodel->insert("Naivas voucher verification in progress.. " , 0, "NAIVAS", json_encode(($token)));
        if (count($token) > 0) {
            $postData = file_get_contents('php://input');
            $post = json_decode($postData, true);

            $transactionid =  $post['transactionid'];
            $amount =  $post['amount'];

            if (empty($transactionid) || empty($amount)) {
                $this->auditmodel->insert("Naivas api tried to verify voucher ref id and failed, reference id or amount not provided", 0, "NAIVAS_INTEGRATION", json_encode($post));
                $this->unauthorized('Data validation failed : Referenceid or amount not provided');
            } else {
                $vouchers = $this->vouchersmodel->get(array('record_state' => 0, 'ref_id' => $transactionid, 'verified' => 0));
                $updated = 0;
                if ($vouchers->num_rows() > 0) {
                    $updated = $this->vouchersmodel->update(array('verified' => 1, 'verifiedon' => date('Y-m-d H:i:s')), array('record_state' => 0, 'ref_id' => $transactionid, 'voucher_used' => 0));
                }
                $this->auditmodel->insert("Naivas api posted voucher vefication for ref id: " . $transactionid . " for amount " . $amount, 0, "NAIVAS_INTEGRATION", json_encode($post));

                if ($vouchers->num_rows() > 0)
                    $this->response(array("resultDesc" => "Success", "resultCode" => "1", "message" => "Source Verified"));
                else
                    $this->unauthorized("No Transaction available.");
            }
        } else
            $this->unauthorized("Invalid Token");
    }

    public function utilized()
    {
        $token = $this->checkToken();
        if (count($token) > 0) {
            $postData = file_get_contents('php://input');
            $post = json_decode($postData, true);

            $transactionid =  $post['transactionid'];
            $dateused =  $post['dateused'];

            if (empty($transactionid) || empty($amount)) {
                $this->auditmodel->insert("Naivas api tried to post voucher usage and failed, reference id or dateused not provided", 0, "NAIVAS_INTEGRATION", json_encode($post));
                $this->unauthorized('Data validation failed : Referenceid or dateused not provided');
            } else {
                $vouchers = $this->vouchersmodel->get(array('record_state' => 0, 'ref_id' => $transactionid, 'voucherused' => 0));
                $updated = 0;
                if ($vouchers->num_rows() > 0) {
                    $updated = $this->vouchersmodel->update(array('voucherused' => 1, 'usedon' => $dateused), array('record_state' => 0, 'ref_id' => $transactionid, 'voucher_used' => 0));
                }
                $this->auditmodel->insert("Naivas api posted voucher usage for ref id: " . $transactionid . " for date " . $dateused, $updated, "NAIVAS_INTEGRATION", json_encode($post));

                if ($vouchers->num_rows() > 0) {
                    $row = $vouchers->row();
                    $this->response(array("resultDesc" => "Success", "resultCode" => "1", "message" => array("mzawadiUsedID" => '', "voucherCode" => $row->voucher_code, "valueUsed" => $row->voucher_value, "dateUsed" => $dateused, "outletCode" => "", "unitCode" => "")));
                } else
                    $this->unauthorized("Transaction already Utilized.");
            }
        } else
            $this->unauthorized("Invalid Token");
    }

    function redeemAtNaivas_frmeng()
    { 
        $postData = file_get_contents('php://input');
        $post = json_decode($postData, true);
        $amount = $post['amount'];
        $client_id =  $post['client_id'];
        $phone_no =  $post['phone_no'];
        $dmo_id =   $post['dmo_id'];
        $key =   $post['dmo_no'];
        $unique =   "N@!v@s @2o3o by mz@w@d!";

        if (empty($amount) || empty($client_id) || empty($phone_no) || empty($dmo_id))
            $this->unauthorized('Required fields are missing.');
        else {
            if ($unique != $key)
            $this->unauthorized('Required fields are missing.');
            else {
                $data = array(
                    'verified' => 0,
                    'record_state' => 0,
                    'voucher_type' => 'Redemption',
                    'dmo_id' => $dmo_id,
                    'CUST_CODE' => '',
                    'voucher_value' => $amount,
                    'addedon' => date('Y-m-d H:i:s'),
                    'verifiedon' => date('Y-m-d H:i:s'),
                    'raw' => '',
                    'client_id' => $client_id,
                    'ref_id' => $this->generaterefid(),
                    'noofpoints' => 0,
                    'expiry' => date('Y-m-d H:i:s', strtotime('+6 months')),
                    'CUST_NAME' => '',
                    'CUST_PHONE' => $phone_no,
                    'voucher_code' => ''
                );

                $rec = $this->vouchersmodel->insert($data);
                if ($rec > 0) {
                    $this->auditmodel->insert("Client redeemed at naivas. Initiating verification.", 1, "NAIVAS_INTEGRATION", json_encode($data));
                    $this->createvoucher($data);
                } else {
                    $this->auditmodel->insert("Client tried to redeem at naivas and failed", 0, "NAIVAS_INTEGRATION", json_encode($data));
                    $this->unauthorized('Redemption failed please try again.');
                }
            }
        }
    } 

    private function generaterefid()
    {
        $code = random_string('alnum', 7);
        $vouchers = $this->vouchersmodel->get(array('record_state' => 0, 'verified' => 0, 'ref_id' => $code));
        if ($vouchers->num_rows() > 0)
            $this->generaterefid();
        return strtoupper($code);
    }

    function createvoucher($data)
    {

        $curl_post_data = array(
            'grant_type' =>  'password',
            'username' =>  NAIVAS_USERNAME,
            'password' =>  NAIVAS_PASSWORD
        );

        $data_string = "";
        foreach ($curl_post_data as $item => $value) {
            $data_string .= $item . "=" . urlencode($value) . "&";
        }

        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, NAIVAS_URL . "token");
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_POST, false);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);
        curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));
        curl_setopt($curl, CURLOPT_HEADER, false);

        $curl_response = curl_exec($curl);
        if ($curl_response === false) {
            $this->auditmodel->insert("Naivas token generation failed " . curl_error($curl), 0, "NAIVAS_INTEGRATION", $data_string);
        } else {

            switch ($http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE)) {
                case 200:
                    $this->auditmodel->insert("Naivas token generated. Continuing to voucher creation.", 1, "NAIVAS_INTEGRATION", $curl_response);
                    $resp = json_decode($curl_response);
                    $token = $resp->access_token;
                    $this->buyCard($token, $data);
                    break;
                default:
                    $this->auditmodel->insert("Naivas error generating token :" . $curl_response . " HTTP : " . $http_code, 0, "NAIVAS_INTEGRATION", $curl_response ." ".$data_string);
                    $this->unauthorized("Token generation failed.");
                    break;
            }
        }
        curl_close($curl);
    }

    function buyCard($token, $data)
    {
        $curl_post_data = array(
            'amount' => $data['voucher_value'],
            'TransactionId' =>  $data['ref_id'],
            'MobileNumber' =>  $data['CUST_PHONE']
        );
        $data_string = json_encode($curl_post_data);

        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, NAIVAS_URL . "api/VoucherAutomation");
        curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type:application/json', 'Authorization:Bearer ' . $token));
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_HEADER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_POST, true);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);

        $curl_response = curl_exec($curl);

        if (curl_error($curl)) {
            $this->auditmodel->insert("Naivas voucher creation failed " . curl_error($curl), 0, "NAIVAS_INTEGRATION", $data_string);
        } else {
            switch ($http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE)) {
                case 200:
                    $resp = json_decode($curl_response);
                    $this->vouchersmodel->update(array('voucher_code' => $resp['VoucherNo'], 'raw' => $curl_response), array('record_state' => 0, 'ref_id' => $data['ref_id']));
                    $recs = $this->db->affected_rows();
                    $this->auditmodel->insert("Naivas created voucher for ref id: " . $data['ref_id'] . " for " . $data['voucher_value'] . " for " .  $data['CUST_PHONE'] . " voucher code " . $resp['VoucherNo'], $recs, "NAIVAS_INTEGRATION", $curl_response);
                    if ($recs > 0) {
                        $this->response(array("resultDesc" => "Success","VoucherNo"=> $resp['VoucherNo'],"resultCode" => "1", "message" => "Voucher created"));
                    } else
                        $this->unauthorized("Transaction already Utilized.");
                    break;
                default:
                    $this->auditmodel->insert("Naivas creation failed :" . $curl_response . " HTTP : " . $http_code, 0, "NAIVAS_INTEGRATION", $curl_response);
                    $this->unauthorized($curl_response);
                    break;
            }
        }
        curl_close($curl);
    }
}
