<?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\Library\Razorpay;
use App\Core\Library\Crypto;
use App\Service\Messaging;
use App\Config\Main;
class PaymentController extends BaseController
{   
    private $FxModel;
    private $OrderModel;
    private $Utilities;
    private $Messaging;
    public function __construct() {
        parent::__construct();
        $this->OrderModel  = new OrderModel();
        $this->FxModel     = new FxModel();
        $this->Utilities   = new Utilities();
        $this->Messaging   = new Messaging(); 
    }

    public function addPaymantLinkRequest() {
        try {
            $rules = [
                'order_id'=> array('rules' => 'required', 'label' => "Order Id"),
                'medium' => array('rules' => 'required', 'label' => "Medium (Email, SMS, WhatsApp)"),
            ];
            $validation = $this->validateReqData($rules, $this->input);            
            if(empty($validation['status'])) {
                throw new \Exception($validation['error']);
            }
            if(!$validation['valid']) {
                $this->sendResponse(array('code' => 400, 'message' => $this->getFirstValidationError(), 'data'=>$validation['validation_error']));
            }
            $order_arr = !is_array($this->input['order_id']) ? [$this->input['order_id']] : $this->input['order_id'];
            $tbl  = $this->FxModel->client_db_prefix.$this->user_data->comp_id . "." . $this->FxModel->tables['payment_gateway'];
            
            $pgrs = $this->FxModel->getRowFromTable($tbl, "isDefault=1 AND status=1", []);
            if(empty($pgrs['status'])) {
                throw new \Exception($pgrs['error']);
            }
            if(empty($pgrs['data'])) {
                $this->sendResponse(array('code' => 400, 'message' => 'Default Payment Gateway settings not found in database', 'data' => null));
            }
            $req_id = $this->Utilities->random_string('alnum');
            $insdata = [];
            foreach($order_arr as $order_id) {
                $insdata[] = [
                    'request_id' => $req_id,
                    'order_id'   => $order_id,
                    'medium'     => strtolower($this->input['medium']),
                    'status'     => 0,
                    'cr_usr'     => $this->user_data->id
                ];
            }
            $tbl = $this->FxModel->client_db_prefix.$this->user_data->comp_id . "." . $this->FxModel->tables['order_payment_link_logs'];
            $insrs = $this->FxModel->batchInsert($tbl, $insdata);
            if(empty($insrs['status'])) {
                throw new \Exception($pgrs['error']);
            }
            $this->Kafkam->sendMessage("sanordo_share_payment_link", ['comp_id'=>$this->user_data->comp_id, 'req_id'=>$req_id]);
            $this->sendResponse(array('code' => 200, 'message' => 'Orders are queued in the process to send Payment link. Please check logs', 'data'=> null)); 
        } catch(\Exception $e) {
            Logger::error($e->getMessage(), ['File' => $e->getFile(), 'Line' => $e->getLine()]);
            $this->sendResponse(array('code' => 400, 'message' => "Something went wrong while sending payment link to client", 'data' => $e->getMessage()));
        }
    }

    public function processReqToSendPaymentLink($comp_id, $req_id) {
        try {
            if(empty($comp_id)) {
                throw new \Exception("company ID is required to update order status");
            }
            if(empty($req_id)) {
                throw new \Exception("Request ID is required to update order status");
            }
            $tbl = $this->FxModel->client_db_prefix.$comp_id . "." . $this->FxModel->tables['order_payment_link_logs'];
            $orsp = $this->FxModel->fetchAllData("SELECT * FROM $tbl WHERE status=:st AND request_id=:req_id ORDER BY cr_date", [":st"=>0, ':req_id'=>$req_id]);
            if(empty($orsp['status'])) {
                throw new \Exception($orsp['error']);
            }
            if(empty($orsp['data'])) {
                throw new \Exception("No Pending logs found to update order status for Request ID:$req_id");
            }
            $tbl = $this->FxModel->client_db_prefix.$comp_id . "." . $this->FxModel->tables['branch_master'];
            $brchrs = $this->FxModel->fetchAllData("SELECT * FROM $tbl WHERE 1=1 ORDER BY branch_id");
            if(!$brchrs['status']) {
                throw new \Exception($brchrs['error']);
            }
            if(empty($brchrs['data'])) {
                throw new \Exception("Branch settings data not found");
            }
            $tbl2 = $this->FxModel->client_db_prefix.$comp_id . "." . $this->FxModel->tables['order_payment_link_logs'];
            foreach($orsp['data'] as $log) {
                $resp = $this->sendPaymentLink($comp_id, $log, $brchrs['data']);
                $failed_reason  = '';
                $status         = 1;
                if(empty($resp['status'])) {
                    Logger::error("Payment Link Failed, Order ID:".$log['order_id']." : ".$resp['error'], ['File' => __FILE__, 'Line' => __LINE__]);
                    $failed_reason = $resp['error'];
                    $status        = 0;
                } else {
                    Logger::debug("Payment Link Success, Order ID:".$log['order_id'], ['File' => __FILE__, 'Line' => __LINE__]);
                }
                
                $updrs = $this->FxModel->update($tbl2, ["status"=>$status, "fail_reason"=>$failed_reason], ["id"=>$log['id']]);
                if(empty($updrs)) {
                    Logger::error($updrs['error'], ['File' => __FILE__, 'Line' => __LINE__]);
                }
            }
            return ["status"=>true, "data"=>NULL];
        } catch(\Exception $e) {
            Logger::error($e->getMessage(), ['File' => $e->getFile(), 'Line' => $e->getLine()]);
            return ["status"=>false, "error"=>$e->getMessage()];
        }
    }

    function sendPaymentLink($comp_id, $log, $branch_list) {
        try {
            $orsp = $this->FxModel->getOrderDetailForCourier($comp_id, "t1.id IN(".$log['order_id'].")", 1);
            if(empty($orsp['status'])) {
                throw new \Exception($orsp['error']);
            }
            if(empty($orsp['data'])) {
                throw new \Exception('Order detail not found');
            }
            if(empty($orsp['data']['net_amt'])) {
                throw new \Exception('Unable to create payment link. The payment amount is zero.');
            }
            if(empty($orsp['data']['customer_detail'])) {
                throw new \Exception('Cusomer information not found in order detail');
            }
            $customer = json_decode($orsp['data']['customer_detail'], true);
            
            if(in_array(strtolower($log['medium']), ['sms', 'whatsapp'])) {
                if(empty($customer['country_code'])) {
                    throw new \Exception('Country code is missing in order detail');
                }
                if(empty($customer['mobile_number'])) {
                    throw new \Exception('Mobile Number is missing in order detail');
                }
            } else if(strtolower($log['medium']) == 'email') {
                if(empty($customer['email_id'])) {
                    throw new \Exception('Email Id is missing in order detail');
                }
            }
            $order_detail = Crypto::getJwtToken(["comp_id"=>$comp_id, "order_id"=>$orsp['data']['id']]);
            $order_token  = Crypto::encryptData($order_detail);
            $link_url = Main::getPublicURL().'/client/paynow?token='.urlencode($order_token);
            $order_branch   = NULL;
            foreach($branch_list as $branch) {
                if($branch['branch_id'] == $orsp['data']['branch_id']) {
                    $order_branch = $branch;
                    break;
                } 
            }
            if(empty($order_branch)) {
                throw new \Exception('Branch detail not found');
            }
            
            if(strtolower($log['medium']) == 'email') { 
                $content = '<a href="' . $link_url . '" >Pay Now</a>';
                $emailrsp = $this->Messaging->sendClientEmail($comp_id, $log['cr_usr'], $order_branch, ["subject"=>"Payment Link", "body"=>$content, "to"=>[$customer['email_id']]], 0);
                if(empty($emailrsp['status'])) {
                    throw new \Exception($emailrsp['error']);
                }
            } else if(strtolower($log['medium']) == 'sms' || strtolower($log['medium']) == 'whatsapp') {
                $settings = [
                    'url' => strtolower($log['medium']) == 'whatsapp' ? $order_branch['whatsapp_url'] : $order_branch['sms_url'],
                    'api_method' => strtolower($log['medium']) == 'whatsapp' ? $order_branch['whatsapp_api_method'] : $order_branch['sms_api_method'],
                    'api_response_type' => strtolower($log['medium']) == 'whatsapp' ? $order_branch['whatsapp_api_response_type'] : $order_branch['sms_api_response_type'],
                    'api_headers' => strtolower($log['medium']) == 'whatsapp' ? $order_branch['whatsapp_api_headers'] : $order_branch['sms_api_headers'],
                    'api_data' => $link_url
                ];
                $msgrsp = $this->Messaging->sendClientMessage($comp_id, $log['cr_usr'], $settings, $log['medium'], NULL, $customer['country_code'].$customer['mobile_number'], NULL);
                if(empty($msgrsp['status'])) {
                    throw new \Exception($msgrsp['error']);
                }
            } else {
                throw new \Exception("Invalid medium selected to send payment link");
            } 
            return ["status"=>true];
        } catch(\Exception $e) {
            Logger::error($e->getMessage(), ['File' => $e->getFile(), 'Line'=>$e->getLine()]);
            return ["status"=>false, "error"=>$e->getMessage()];
        }
    }

    private function getPaymentPageData($comp_id, $order_id) {
        try {
            $orsp = $this->FxModel->getOrderDetailForCourier($comp_id, "t1.id IN(".$order_id.")", 1);
            if(empty($orsp['status'])) {
                throw new \Exception($orsp['error']);
            }
            if(empty($orsp['data'])) {
                throw new \Exception('Order detail not found');
            }
            if (!empty($orsp['data']['shopify_id'])) {
                throw new \Exception('Order detail not found');
            }
            $tbl  = $this->FxModel->client_db_prefix.$comp_id . "." . $this->FxModel->tables['payment_gateway'];
            $pgrs = $this->FxModel->getRowFromTable($tbl, "api=:api_type AND status=1", [':api_type'=>'razorpay']);
            if(empty($pgrs['status'])) {
                throw new \Exception($pgrs['error']);
            }
            if(empty($pgrs['data'])) {
                Logger::error('Razorpay Payment Gateway settings not found or not active in database', ['File' => __FILE__, 'Line' => __LINE__]);
                throw new \Exception('Unable to get Payment Gateway');
            }
            if(empty($pgrs['data']['key']) || empty($pgrs['data']['secret'])) {
                Logger::error('Key or Secret not found in razorpay payment gateway settings', ['File' => __FILE__, 'Line' => __LINE__]);
                throw new \Exception('Unable to get Payment Gateway');
            }
            if ($orsp['data']['partial_payment'] > 0) {
                $amount = ($orsp['data']['partial_payment']) * 100;
            } else {
                $amount = ($orsp['data']['net_amt']) * 100;
            }
            if(empty($amount)) {
                throw new \Exception('Unable to get Payment amount.');
            }
            if(empty($orsp['data']['razorpay_order_id'])) {
                $rpay = new Razorpay($pgrs['data']);
                $rordrsp = $rpay->createOrder($orsp['data']['order_id'], $amount);
                if(empty($rordrsp['status'])) {
                    throw new \Exception($rordrsp['error']);
                }
                $updata = [
                    'paymentgateway' => $pgrs['data']['id'],
                    'razorpay_order_id'=>$rordrsp['data'], 
                    'payment_confirm_id'=>NULL, 
                    'payment_response_json'=>NULL, 
                    'payment_status'=>NULL
                ];
                $upd = $this->OrderModel->updateOrder($comp_id, $updata, "id=:id", [':id'=>$orsp['data']['id']]);
                if(empty($upd['status'])) {
                    throw new \Exception($upd['error']);
                }
                $orsp['data']['razorpay_order_id'] = $rordrsp['data'];
            }
            $customer = json_decode($orsp['data']['customer_detail'], true);
            $vdata = [
                "order_id"      => $orsp['data']['razorpay_order_id'],
                "payment_confirm_id"  => $orsp['data']['payment_confirm_id'],
                "api_key"       => $pgrs['data']['key'],
                "api_secret"    => $pgrs['data']['secret'],
                "receipt"       => $orsp['data']['id'],
                "current_order_status"  => $orsp['data']['current_order_status'],
                "amount"        => $amount,
                "currency"      => 'INR',
                "product"       => !empty($orsp['data']['courier_slip_product_name']) ? $orsp['data']['courier_slip_product_name'] : "Order - " . $orsp['data']['customer_order_id'],
                "product_description"   => "",
                "comp_logo"     => !empty($orsp['data']['logo_img']) ? base_url($orsp['data']['logo_img']) : "",
                "profile_name"  => $customer['customer_name'],
                "profile_email" => $customer['email_id'],
                "profile_contact" => $customer['country_code'].$customer['mobile_number'],
                "isSaved"       => !empty($orsp['data']['payment_confirm_id'])?true:false,
                "payment_status"  => $orsp['data']['payment_status'],
            ];
            return ["status"=>true, "data"=>$vdata];
        } catch(\Exception $e) {
            Logger::error($e->getMessage(), ['File' => $e->getFile(), 'Line' => $e->getLine()]);
           return ["status"=>false, "error"=>$e->getMessage()];
        }
    }

    #[Route(method: 'GET', path: '/client/paynow')]
    public function initiatePaymentProcess() {
        try {
            $rules = ['token'=> array('rules' => 'required', 'label' => "Payment Token")];
            $validation = $this->validateReqData($rules, $this->input);            
            if(empty($validation['status'])) {
                throw new \Exception($validation['error']);
            }
            if(!$validation['valid']) {
                throw new \Exception($this->getFirstValidationError());
            }
            $jwt = Crypto::decrytData($this->input['token']);
            if(empty($jwt)) {
                throw new \Exception("Unable to get token in the request.");
            }
            $req = Crypto::decryptJwtToken($jwt);
            
            if(empty($req)) {
                throw new \Exception("Unable to get order information in the request.");
            }
            if(empty($req->comp_id) || empty($req->order_id)) {
                throw new \Exception("Unable to get client id OR order id in the request.");
            }
            $vrs = $this->getPaymentPageData($req->comp_id, $req->order_id);
            if(empty($vrs['status'])) {
                throw new \Exception($vrs['error']);
            }
            view('payment/razorpay', $vrs['data'], true);
        } catch(\Exception $e) {
            Logger::error($e->getMessage(), ['File' => $e->getFile(), 'Line' => $e->getLine()]);
            view('payment/error', ["error"=>$e->getMessage()], true);
        }
    }

    #[Route(method:'POST', path: '/client/paynow')]
    public function savePaymentResponse() {
        try {
            $rules = ['token'=> array('rules' => 'required', 'label' => "Payment Token")];
            $validation = $this->validateReqData($rules, $this->input);            
            if(empty($validation['status'])) {
                throw new \Exception($validation['error']);
            }
            if(!$validation['valid']) {
                throw new \Exception($this->getFirstValidationError());
            }
            $jwt = Crypto::decrytData($this->input['token']);
            if(empty($jwt)) {
                throw new \Exception("Unable to get token in the request.");
            }
            $req = Crypto::decryptJwtToken($jwt);
            
            if(empty($req)) {
                throw new \Exception("Unable to get order information in the request.");
            }
            if(empty($req->comp_id) || empty($req->order_id)) {
                throw new \Exception("Unable to get client id OR order id in the request.");
            }
            $upd = ['payment_status' => 'FAILED'];
            if(!empty($this->input['razorpay_payment_id'])) {
                $upd['payment_confirm_id'] = $this->input['razorpay_payment_id'];
                $upd['payment_status'] = 'SUCCESS';
                
                $vdata['payment_status'] = 'SUCCESS';
                $vdata['razorpay_payment_id'] = $this->input['razorpay_payment_id'];
                $vdata['isSaved'] = true;
            }
            $upd = $this->OrderModel->updateOrder($req->comp_id, $upd, "id=:id", [':id'=>$req->order_id]);
            if(empty($upd['status'])) {
                throw new \Exception($upd['error']);
            }
            $vrs = $this->getPaymentPageData($req->comp_id, $req->order_id);
            if(empty($vrs['status'])) {
                throw new \Exception($vrs['error']);
            }
            view('payment/razorpay', $vrs['data'], true);
        } catch(\Exception $e) {
            Logger::error($e->getMessage(), ['File' => $e->getFile(), 'Line' => $e->getLine()]);
            view('payment/error', ["error"=>$e->getMessage()], true);
        }
    }
}