<?php
namespace App\Controllers\Client;

use App\Core\Attributes\Route;
use App\Controllers\BaseController;
use App\Core\Service\Logger;
use App\Model\FxModel;
use App\Model\OrderModel;
use App\Library\Utilities;
use App\Core\Library\Crypto;
use App\Library\Shopify;
class WebhookController extends BaseController
{
    private $FxModel;
    private $OrderModel;
    private $Utilities;
    public function __construct()
    {
        parent::__construct();
        $this->OrderModel  = new OrderModel();
        $this->FxModel     = new FxModel();
        $this->Utilities   = new Utilities();
        
        $hd = [];
        if(!empty($this->headers)) {
            foreach($this->headers as $key=>$val) {
                $hd[strtolower($key)] = $val;
            }
        }
        $this->headers = $hd;
    }

    public function sendResponse(array $resp)
    {
        try {
            http_response_code($resp['code']);
            $empdata = json_encode($resp);
            echo $empdata;
            exit;
        } catch (\Exception $e) {
            Logger::error($e->getMessage(), ['File' => $e->getFile(), 'Line' => $e->getLine()]);
        }
    }

    #[Route(method: 'POST', path: '/webhook/razorpay')]
    public function onRazorpayEvents()
    {
        try {
            if (empty($this->input['token'])) {
                throw new \Exception("Unable to get token in the shiprocket webhook request.");
            }
            $client = $this->FxModel->getRowFromTable($this->FxModel->tables['admin_client_master'], "access_token=:token", [':token' => $this->input['token']]);
            if (empty($client['status'])) {
                throw new \Exception("Unable to get client information in the shiprocket webhook request.");
            }
            if (empty($client['data']['id'])) {
                throw new \Exception("Unable to get client id the shiprocket webhook request.");
            }

            $input = file_get_contents("php://input");
            $insarr = ["api" => "razorpay", "payload" => $input];
            $tbl  = $this->FxModel->client_db_prefix . $client['data']['id'] . "." . $this->FxModel->tables['payment_webhook_logs'];
            $inrs = $this->FxModel->insert($tbl, $insarr);
            if (empty($inrs['status'])) {
                Logger::error($inrs['error'], ['File' => __FILE__, 'Line' => __LINE__]);
            }
            $post_data = json_decode($input, true);
            if (empty($post_data['event'])) {
                throw new \Exception("Event name is not getting received in the payment webhook request.");
            }
            $order_id = $post_data['payload']['payment']['entity']['order_id'];
            if (empty($order_id)) {
                throw new \Exception("Unable to get order id the payment webhook request.");
            }
            if (in_array(strtolower($post_data['event']), ['payment.failed', 'payment.captured'])) {
                $updarr = [
                    "payment_confirm_id" => $post_data['payload']['payment']['entity']['id'],
                    "payment_response_json" => $input,
                    "payment_status" => strtolower($post_data['event']) == 'payment.captured' ? 'SUCCESS' : 'FAILED'
                ];
                $upd = $this->OrderModel->updateOrder($client['data']['id'], $updarr, "razorpay_order_id=:id", [':id' => $order_id]);
                if (empty($upd['status'])) {
                    throw new \Exception($upd['error']);
                }
            }
            $this->sendResponse(array('code' => 200, 'message' => "Payment webhook payload procesed successfully"));
        } catch (\Exception $e) {
            Logger::error($e->getMessage(), ['File' => $e->getFile(), 'Line' => $e->getLine()]);
            $this->sendResponse(array('code' => 500, 'message' => "Something went wrong while processing webhook payload", 'error' => $e->getMessage()));
        }
    }

    #[Route(method: 'POST', path: '/webhook/courier')]
    public function onShiprocketEvents()
    {
        try {
            if (empty($this->input['token'])) {
                throw new \Exception("Unable to get token in the shiprocket webhook request.");
            }
            $client = $this->FxModel->getRowFromTable($this->FxModel->tables['admin_client_master'], "access_token=:token", [':token' => $this->input['token']]);
            if (empty($client['status'])) {
                throw new \Exception("Unable to get client information in the shiprocket webhook request.");
            }
            if (empty($client['data']['id'])) {
                throw new \Exception("Unable to get client id the shiprocket webhook request.");
            }

            $payload = file_get_contents("php://input");
            Logger::debug($payload, ['File' => __FILE__, 'Line' => __LINE__]);
            $input = json_decode($payload, true);
            if (empty($input)) {
                $this->sendResponse(array('code' => 200, 'message' => "Webhook Payload is not getting received"));
            }
            if (empty($input['awb'])) {
                $this->sendResponse(array('code' => 200, 'message' => "AWB Number is not getting received in webhook payload"));
            }
            if (empty($this->headers['x-api-key'])) {
                $this->sendResponse(array('code' => 200, 'message' => "API Key not received in request headers", "data"=>$this->headers));
            }
            $crsp = $this->FxModel->getCourierDetail($client['data']['id'], 't1.id="' . $this->headers['x-api-key'] . '"');
            if (empty($crsp['status'])) {
                throw new \Exception($crsp['error']);
            }
            if (empty($crsp['data'])) {
                $this->sendResponse(array('code' => 200, 'message' => 'Invalid x-api-key in request header. Courier detail not found', 'data' => null));
            }
            $logtbl = $this->FxModel->client_db_prefix . $client['data']['id'] . '.' . $this->FxModel->tables['order_status_update_webhook_log'];
            $logdata = [
                "courier_api" => 'shiprocket',
                "docket_no" => $input['awb'],
                "payload" => $payload
            ];
            $log_id = 0;
            $logrs = $this->FxModel->insert($logtbl, $logdata);
            if (empty($logrs['status'])) {
                Logger::error($logrs['error'], ['File' => __FILE__, 'Line' => __LINE__]);
            } else {
                $log_id = $logrs['insert_id'];
            }
            $orsp = $this->OrderModel->getOrderDetail($client['data']['id'], 'docket_number="' . $input['awb'] . '"', 1);
            if (empty($orsp['status'])) {
                throw new \Exception($orsp['error']);
            }
            if (empty($orsp['data'])) {
                $this->sendResponse(array('code' => 200, 'message' => 'Order detail not found for AWB Number:' . $input['awb'], 'data' => null));
            }
            $data = [];
            $upda = [];
            $status_type = 0;
            $msg  = "";
            switch ((int)$input['shipment_status_id']) {
                case 103:
                    $msg = $input['current_status'] . ", this status come before Field Pickup Done";
                    break;
                case 21:
                    $data['remarks'] = 'Undelivered By Shiprocket';
                    $status_type = 20;
                    break;
                case 19:
                    $data['remarks'] = 'Field Pickup Done By Shiprocket';
                    $status_type = 16;
                    break;
                case 17:
                    $data['remarks'] = 'Out For Delivery By Shiprocket';
                    $status_type = 2;
                    if (empty($orsp['data']['delivery_attempt'])) {
                        $upda['delivery_attempt'] = 1;
                    } else {
                        $upda['delivery_attempt'] += 1;
                    }
                case 10:
                    $data['remarks'] = 'Returned By Shiprocket';
                    $status_type = 8;
                    break;
                case 7:
                    $data['remarks'] = 'Delivered By Shiprocket';
                    $status_type = 19;
                    break;
                default:
                    $data['remarks'] = 'In Transit By Shiprocket';
                    $status_type = 17;
                    break;
            }
            if ($status_type == 17 && $orsp['data']['status_type'] == 17) {
                $msg = $input['current_status_id'] . ", this status store in Transit";
            }

            if (empty($msg)) {
                $strs = $this->changeOrderStatus($client['data']['id'], $input, $status_type, $orsp['data'], $data);
                if (empty($strs['status'])) {
                    throw new \Exception($strs['error']);
                }
                if (!empty($strs['msg'])) {
                    Logger::debug($strs['msg'], ['File' => __FILE__, 'Line' => __LINE__]);
                    $msg = $strs['msg'];
                }
            }

            if (!empty($log_id)) {
                $logupd = $this->FxModel->update($logtbl, ['order_id' => $orsp['data']['id'], "response" => $msg ? $msg : 'Shiprocket webhook payload procesed successfully'], ["id" => $log_id]);
                if (empty($logupd['status'])) {
                    Logger::error($logupd['error'], ['File' => __FILE__, 'Line' => __LINE__]);
                }
            }

            if (!empty($msg)) {
                $this->sendResponse(array('code' => 200, 'message' => $msg, 'data' => null));
            } else {
                $this->sendResponse(array('code' => 200, 'message' => "Shiprocket webhook payload procesed successfully"));
            }
        } catch (\Exception $e) {
            Logger::error($e->getMessage(), ['File' => $e->getFile(), 'Line' => $e->getLine()]);
            $this->sendResponse(array('code' => 200, 'message' => "Something went wrong while processing webhook payload", 'error' => $e->getMessage()));
        }
    }

    #[Route(method: 'POST', path: '/webhook/sanpbx')]
    public function onCallEvents()
    {
        try {
            Logger::debug(json_encode($this->input), ['File' => __FILE__, 'Line' => __LINE__]);
            if (!empty($this->input['token'])) {
                if (empty($this->input['token'])) {
                    throw new \Exception("Unable to get token in the call webhook request.");
                }
                $client = $this->FxModel->getRowFromTable($this->FxModel->tables['admin_client_master'], "access_token=:token", [':token' => $this->input['token']]);
                if (empty($client['status'])) {
                    throw new \Exception("Unable to get client information in the shiprocket webhook request.");
                }
                if (empty($client['data']['id'])) {
                    throw new \Exception("Unable to get client id the shiprocket webhook request.");
                }

                $comp_id = $client['data']['id'];
                $refData = json_decode($this->input['userfield']);
                $dbData['ref_type'] = $refData->ref_type;
                $dbData['ref_id'] = $refData->ref_id;
                $dbData['user_id'] = $refData->user_id;
                $dbData['calltype'] = $this->input['call_type'];
                $dbData['source'] = $this->input['source'];
                $dbData['disposition'] = $this->input['disposition'];
                $dbData['calldate'] = $this->input['start_time'];
                $dbData['duration'] = $this->input['durn'];
                $dbData['billsec'] = $this->input['billsec'];
                $dbData['recfile'] = $this->input['recfile'];
                $db = $this->FxModel->client_db_prefix . $comp_id;
                $resp = $this->FxModel->insert("`$db`.`call_logs`", $dbData);
                if (!$resp['status']) {
                    Logger::error($resp['error'], ['File' => __FILE__, 'Line' => __LINE__]);
                } else {
                    Logger::debug('Inserted ID - '.$resp['insert_id'], ['File' => __FILE__, 'Line' => __LINE__]);
                }
            } else {
                Logger::error('Token Missing (Webhook - onCallEvents())', ['File' => __FILE__, 'Line' => __LINE__]);
            }
            $this->sendResponse(array('code' => 200, 'message' => "call events received"));
        } catch (\Exception $e) {
            $this->sendResponse(array('code' => 400, 'message' => "Something went wrong while processing call events", 'error' => $e->getMessage()));
        }
    }
    
    #[Route(method: 'POST', path: '/webhook/shopify')]
    public function onShopifyOrderEvent()
    {
        try {
            if (!isset($_SERVER['HTTP_X_SHOPIFY_HMAC_SHA256'])) {
                Logger::debug("HTTP_X_SHOPIFY_HMAC_SHA256 not received", ['File' => __FILE__, 'Line' => __LINE__]);
                throw new \Exception("HTTP_X_SHOPIFY_HMAC_SHA256 not received");
            }
            if (empty($this->input['token'])) {
                throw new \Exception("Unable to get token in the shiprocket webhook request.");
            }
            $client = $this->FxModel->getRowFromTable($this->FxModel->tables['admin_client_master'], "access_token=:token", [':token' => $this->input['token']]);
            if (empty($client['status'])) {
                throw new \Exception("Unable to get client information in the shiprocket webhook request.");
            }
            if (empty($client['data']['id'])) {
                throw new \Exception("Unable to get client id the shiprocket webhook request.");
            }

            $payload = file_get_contents("php://input");
            Logger::debug('', ['File' => __FILE__, 'Line' => __LINE__]);
            Logger::debug($payload, ['File' => __FILE__, 'Line' => __LINE__]);

            $shopify = new Shopify($client['data']);
            $is_valid = $shopify->verifyWebhook($payload, trim($_SERVER['HTTP_X_SHOPIFY_HMAC_SHA256']), trim($client['data']['webhook_key']));
            if (!$is_valid) {
                Logger::debug("Shopify Webhook verification failed", ['File' => __FILE__, 'Line' => __LINE__]);
                throw new \Exception("Shopify Webhook verification failed");
            }
            $input = json_decode($payload, true);
            Logger::debug("Shopify Webhook verification success", ['File' => __FILE__, 'Line' => __LINE__]);
            $orsp = $this->FxModel->getOrderDetailForCourier($client['data']['id'], "t1.shopify_id=".$input['order_id'], 1);
            if(empty($orsp['status'])) {
                throw new \Exception($orsp['error']);
            }
            if(empty($orsp['data'])) {
                throw new \Exception('Order detail not found for Shopify Order ID:' . $input['order_id']);
            }
            $data = [];
            $upda = [];
            $status_type = 0;
            $msg  = "";
            if($input['status'] == "success") {
                $data['remarks'] = 'Order Fulfillment by shopify';
                $status_type = 14; // Shopify Order Fulfilled

                $upda = [
                    "remarks"=>"Order fulfilled by Shopify",
                    "docket_number"=>$input['tracking_number'],
                    "courier_name"=>$input['courier_name']  
                ];
                $tbl = $this->FxModel->client_db_prefix . $client['data']['id'] . '.' . $this->FxModel->tables['order_sorder'];
                $uprs = $this->FxModel->update($tbl, $upda, ["id"=>$orsp['data']['id']]);
                if (empty($uprs['status'])) {
                    throw new \Exception($uprs['error']);
                }
                $upd = ['used_time' => date('Y-m-d H:i:s'), 'order_id' => $orsp['data']['id']];
                $uprs = $this->FxModel->updateDocketNumberUsage($client['data']['id'], $input['tracking_number'], $upd);
                if (empty($uprs['status'])) {
                    throw new \Exception($uprs['error']);
                }
                Logger::debug("Shopify Order ID: #" . $input['order_id'] . ", Order status updated to Delivered", ['File' => __FILE__, 'Line' => __LINE__]);
            } else {
                $data['remarks'] = 'Order Not Fulfillment by shopify';
                $status_type = 13; // Shopify Order Not Fulfilled
            }
            $strs = $this->changeOrderStatus($client['data']['id'], $input, $status_type, $orsp['data'], $data);
            if (empty($strs['status'])) {
                throw new \Exception($strs['error']);
            }
            if (!empty($strs['msg'])) {
                Logger::debug($strs['msg'], ['File' => __FILE__, 'Line' => __LINE__]);
                $msg = $strs['msg'];
            }
            if (!empty($msg)) { 
                $this->sendResponse(array('code' => 200, 'message' => $msg, 'data' => null));
            } else {
                $this->sendResponse(array('code' => 200, 'message' => "Shopify webhook payload procesed successfully"));
            }
        } catch (\Exception $e) {           
            $this->sendResponse(array('code' => 400, 'message' => "Something went wrong while processing shopify webhook", 'error' => $e->getMessage()));
        }
    }   

    private function changeOrderStatus($comp_id, $input, $status_type, $order, $data)
    {
        try {
            $msg = "";
            $next_status = NULL;
            $nextstrs = $this->OrderModel->getNextOrderList($comp_id, $order['current_order_status']);
            if (empty($nextstrs['status'])) {
                throw new \Exception($nextstrs['error']);
            }
            if (empty($nextstrs['data'])) {
                $msg = $input['current_status_id'] . ", Wrong Order Status Selected. Order is end of the status stage";
            }
            foreach ($nextstrs['data'] as $st) {
                if ($st['status_type'] == $status_type) {
                    $next_status = $st;
                }
            }
            if (empty($next_status)) {
                $msg = $input['current_status_id'] . ", Wrong Order Status Selected.";
            }

            if ($next_status['stock_in_out'] == 2) { //Stock Out
                $itmrs = $this->OrderModel->getStockDetail($comp_id, [], $order['branch_id'], "t3.order_id=" . $order['id'], $order);
                if (empty($itmrs['status'])) {
                    throw new \Exception($itmrs['error']);
                }
                foreach ($itmrs['data'] as $itm) {
                    if (!isset($itm['stock_qty'])) {
                        $msg = 'Invalid Item stock found for item ' . $itm['item_name'];
                        break;
                    }
                    if ($itm['ordered_qty'] > ($itm['stock_qty']) && $itm['allow_negative_stock'] != 1) {
                        $msg = "Not in stock for that much quantity, you can add maximum of " . $itm['stock_qty'];
                        break;
                    }
                }
            }
            
            if (empty($msg)) {
                $data['current_order_status'] = $next_status['status_action_id'];
                $log_message = "Updated Order Status to " . $next_status['status_name'] . " Order ID - #" . $order['id'] . " BY :: " . "WEBHOOK";
                $updrs = $this->FxModel->updateOrderStatus($comp_id, 0, $order, $data, $next_status, $log_message);

                if (empty($updrs['status'])) {
                    throw new \Exception($updrs['error']);
                }

                $data['update_order_id'] = $order['id'];

                $udata  = ['is_open' => 1, 'opened_by' => NULL, 'opened_time' => NULL];
                $where  = "is_open = 0 AND id IN(" . $order['id'] . ")";

                $resetrs = $this->OrderModel->updateOrder($comp_id, $udata, $where, []);
                if (empty($resetrs['status'])) {
                    Logger::error($resetrs['error'], ['File' => __FILE__, 'Line' => __LINE__]);
                }
                Logger::debug("OrderId: #" . $order['id'] . ", Order status updated and opened order status reset successfully", ['File' => __FILE__, 'Line' => __LINE__]);
            }
            return ["status" => true, "msg" => $msg];
        } catch (\Exception $e) {
            Logger::error($e->getMessage(), ['File' => $e->getFile(), 'Line' => $e->getLine()]);
            return ["status" => false, "error" => $e->getMessage()];
        }
    }
}
