<?php
declare(strict_types=1);

namespace App\Lib;

use Aws\S3\S3Client;
use Aws\MediaConvert\MediaConvertClient;
use Aws\Exception\AwsException;
use Aws\S3\Exception\S3Exception;
use App\Lib\Ffmpeg;
use App\Lib\Utility;

class Utility
{
    public static function isJsonError(string $data): string {
        json_decode($data);
        switch (json_last_error()) {
            case JSON_ERROR_NONE:
                return "false";
            case JSON_ERROR_DEPTH:
                return ' - Maximum stack depth exceeded';
            case JSON_ERROR_STATE_MISMATCH:
                return ' - Underflow or the modes mismatch';
            case JSON_ERROR_CTRL_CHAR:
                return ' - Unexpected control character found';
            case JSON_ERROR_SYNTAX:
                return ' - Syntax error, malformed JSON';
            case JSON_ERROR_UTF8:
                return ' - Malformed UTF-8 characters, possibly incorrectly encoded';
            default:
                return ' - Unknown error';
        }
    }
    public static function uploadAMultipartFileIntoFolder(string $param, ?string $ext, string $folder): string|false {
        $fileName = uniqid();
        if (!file_exists($folder)) {
            mkdir($folder, 0777, true);
        }
        $extension = $_FILES[$param]['type'];
        $parts = explode('/', $extension);
        $ext = array_pop($parts);
        if ($ext === "mpeg") {
            $ext = "mp3";
        }
        $ext = "." . $ext;
        $filePath = $folder . "/" . $fileName . $ext;
        if (!file_exists($folder)) {
            mkdir($folder, 0777, true);
        }
        if (move_uploaded_file($_FILES[$param]['tmp_name'], $filePath)) {
            return $filePath;
        } else {
            return false;
        }
    }
    public static function uploadMapImageintoFolder(string|int $user_id, string $lat, string $long): string|false {
        $folder_url = UPLOADS_FOLDER_URI;
        $url = "https://maps.googleapis.com/maps/api/staticmap?path=color:black|weight:5|&size=700x350&key=" . GOOGLE_MAPS_KEY . "&markers=color:red|" . $lat . "," . $long;
        $content = @file_get_contents($url);
        if (!strpos($http_response_header[0], "200")) {
            return false;
        }
        $file = file_get_contents($url);
        $fileName = uniqid();
        $folder = $folder_url . '/' . $user_id;
        if (!file_exists($folder)) {
            mkdir($folder, 0777, true);
        }
        $filePath = $folder . "/" . $fileName . '.png';
        file_put_contents($filePath, $file);
        return $filePath;
    }
    public static function getPlaceDetails(string $place_id): array {
        $url = "https://places.googleapis.com/v1/places/$place_id?fields=id,displayName&key=AIzaSyAabs3BFfoIUmP9hgH-Vfeu7S7FoCtQ4wU";
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_PROXYPORT, 3128);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        $response = curl_exec($ch);
        curl_close($ch);
        return json_decode($response, true);
    }
    public static function getRecommendedVideos($user_id, string $lat, string $long, int $limit, int $radius, string $type): array {
        if (strlen($lat) < 3) {
            $data = json_encode([
                'data' => [
                    'userId' => $user_id,
                    'type' => $type,
                    'limit' => $limit,
                ]
            ]);
        } else {
            $data = json_encode([
                'data' => [
                    'lat' => $lat,
                    'long' => $long,
                    'userId' => $user_id,
                    'radius' => $radius,
                    'type' => $type,
                    'limit' => $limit,
                ]
            ]);
        }
        $ch = curl_init(AI_RECOMMENDED_VIDEOS_URL);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json'
        ]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        $response = curl_exec($ch);
        curl_close($ch);
        return json_decode($response, true);
    }
    public static function checkMissingParams(array $params, array $requiredParams): array {
        $missingParams = [];
        foreach ($requiredParams as $requiredParam) {
            if (!array_key_exists($requiredParam, $params)) {
                $missingParams[] = $requiredParam;
            }
        }
        return $missingParams;
    }
    public static function isValidJson(string $string): bool {
        json_decode($string);
        return (json_last_error() === JSON_ERROR_NONE);
    }
    public static function getCountryCityProvinceFromLatLong(string $lat, string $long): array {
        $url = 'https://maps.googleapis.com/maps/api/geocode/json?key=' . GOOGLE_MAPS_KEY . '&latlng=' . $lat . ',' . $long . '&sensor=false';
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_PROXYPORT, 3128);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        $response = curl_exec($ch);
        curl_close($ch);
        $info = [
            'country' => "",
            'state' => "",
            'city' => "",
            'location_string' => "",
            'lat' => $lat,
            'long' => $long,
            'street_num' => 0,
            'street_name' => "street",
            'zip' => "54000",
            'long_city' => "",
            'short_state' => "",
            'short_country' => "",
            'place_id' => ""
        ];
        $output_array = json_decode($response, true);
        $output = json_decode($response);
        if (count($output_array["results"]) > 0) {
            for ($j = 0; $j < count($output_array['results'][0]['address_components']); $j++) {
                $cn = [$output_array['results'][0]['address_components'][$j]['types'][0]];
                if (strlen($info['country']) < 1) {
                    if (in_array("country", $cn)) {
                        $long_country = $output_array['results'][0]['address_components'][$j]['long_name'];
                        $short_country = $output_array['results'][0]['address_components'][$j]['short_name'];
                        $info['long_country'] = $long_country;
                        $info['short_country'] = $short_country;
                    }
                }
                if (strlen($info['city']) < 1) {
                    if (in_array("locality", $cn)) {
                        $long_city = $output_array['results'][0]['address_components'][$j]['long_name'];
                        $short_city = $output_array['results'][0]['address_components'][$j]['short_name'];
                        $info['long_city'] = $long_city;
                        $info['short_city'] = $short_city;
                    }
                }
                if (strlen($info['state']) < 1) {
                    if (in_array("administrative_area_level_1", $cn)) {
                        $long_state = $output_array['results'][0]['address_components'][$j]['long_name'];
                        $short_state = $output_array['results'][0]['address_components'][$j]['short_name'];
                        $info['short_state'] = $short_state;
                        $info['long_state'] = $long_state;
                    }
                }
                if (in_array("street_number", $cn)) {
                    $street_number = $output_array['results'][0]['address_components'][$j]['long_name'];
                    $info['street_num'] = $street_number;
                }
                if (in_array("route", $cn)) {
                    $street_name = $output_array['results'][0]['address_components'][$j]['long_name'];
                    $info['street_name'] = $street_name;
                }
                if (in_array("postal_code", $cn)) {
                    $postal_code = $output_array['results'][0]['address_components'][$j]['long_name'];
                    $info['zip'] = $postal_code;
                }
                $place_id = $output_array['results'][0]['place_id'];
                $info['place_id'] = $place_id;
            }
            $info['location_string'] = $output_array["results"][0]['formatted_address'];
        } else {
            $info['country'] = "";
            $info['state'] = "";
            $info['city'] = "";
            $info['location_string'] = "";
            if (is_array($output_array)) {
                if (array_key_exists('error_message', $output_array)) {
                    $info['output'] = $output_array['error_message'];
                    $info['location_string'] = "";
                }
            } else {
                $info['output'] = "check manuall";
                $info['location_string'] = "";
            }
        }
        return $info;
    }
    public static function getLocationFromIP(?string $ip = null): array {
        $url = "http://www.geoplugin.net/json.gp?ip=" . ($ip ?? $_SERVER["REMOTE_ADDR"]);
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_PROXYPORT, 3128);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        $response = curl_exec($ch);
        curl_close($ch);
        $output_array = json_decode($response, true);
        return $output_array;
    }
    public function getPaymentIntent(string $pi): array {
        try {
            $stripe = new \Stripe\StripeClient(STRIPE_KEY);
            $details = $stripe->paymentIntents->retrieve($pi, []);
            $output['code'] = 200;
            $output['msg'] = $details->jsonSerialize();
            return $output;
        } catch (\Stripe\Exception\ApiErrorException $e) {
            $error = self::access_protected_object($e, "error");
            $output['code'] = 201;
            $output['msg'] = $error['message'];
            return $output;
        }
    }
    public static function access_protected_object(object $obj, string $prop): mixed {
        $reflection = new ReflectionClass($obj);
        $property = $reflection->getProperty($prop);
        $property->setAccessible(true);
        return $property->getValue($obj);
    }
    public function getStripeCheckoutSession(string $session_id): array {
        try {
            $stripe = new \Stripe\StripeClient(STRIPE_KEY);
            $checkout_session = $stripe->checkout->sessions->retrieve($session_id, []);
            $output['code'] = 200;
            $output['msg'] = $checkout_session->jsonSerialize();
            return $output;
        } catch (\Stripe\Exception\ApiErrorException $e) {
            $error = self::access_protected_object($e, "error");
            $output['code'] = 201;
            $output['msg'] = $error['message'];
            return $output;
        }
    }
    public function chargeCustomerAgainstPaymentID(float|int $amount, string $payment_id, string $customer_id, array $invoice_items): array {
        try {
            \Stripe\Stripe::setApiKey(STRIPE_KEY);
            $payment_intent = \Stripe\PaymentIntent::create([
                'amount' => $amount * 100,
                'currency' => 'usd',
                'payment_method' => $payment_id,
                'customer' => $customer_id,
                'metadata' => $invoice_items
            ]);
            if ($payment_intent->status == 'requires_confirmation') {
                $payment_intent->confirm();
                if ($payment_intent->status == 'succeeded') {
                    $code = 200;
                    $msg = $payment_intent;
                } else {
                    echo 'Payment failed: ' . $payment_intent->status;
                    $code = 201;
                    $msg = $payment_intent->status;
                }
            } else if ($payment_intent->status == 'succeeded') {
                $code = 200;
                $msg = $payment_intent;
            } else {
                $code = 201;
                $msg = $payment_intent->status;
            }
            $output['code'] = $code;
            $output['msg'] = $msg;
            return $output;
        } catch (\Stripe\Exception\ApiErrorException $e) {
            $error = self::access_protected_object($e, "error");
            $output['code'] = 201;
            $output['msg'] = $error['message'];
            return $output;
        }
    }
    public function stripeCheckoutSession(mixed $price_id, string $email, string $order_id, string $customer, string $token): array {
        try {
            $stripe = new \Stripe\StripeClient(STRIPE_KEY);
            if (strlen($customer) > 5) {
                $checkout_session = $stripe->checkout->sessions->create([
                    'success_url' => BASE_URL . "/cart/success/$token/" . $order_id,
                    'cancel_url' => BASE_URL . "/cart/step3/$token",
                    'client_reference_id' => $order_id,
                    'customer' => $customer,
                    'line_items' => $price_id,
                    'payment_intent_data' => [
                        'setup_future_usage' => 'off_session',
                    ],
                    'mode' => 'payment',
                ]);
            } else {
                $checkout_session = $stripe->checkout->sessions->create([
                    'success_url' => BASE_URL . "/cart/success/$token/" . $order_id,
                    'cancel_url' => BASE_URL . "/cart/step3/$token",
                    'customer_email' => $email,
                    'client_reference_id' => $order_id,
                    'customer_creation' => 'always',
                    'line_items' => $price_id,
                    'payment_intent_data' => [
                        'setup_future_usage' => 'off_session',
                    ],
                    'mode' => 'payment',
                ]);
            }
            $output['code'] = 200;
            $output['msg'] = $checkout_session->jsonSerialize();
            return $output;
        } catch (\Stripe\Exception\ApiErrorException $e) {
            $error = self::access_protected_object($e, "error");
            $output['code'] = 201;
            $output['msg'] = $error['message'];
            return $output;
        }
    }
    public static function createStripeToken(string $number, int $exp_month, int $exp_year, string $cvc, string $name): array {
        try {
            $stripe = new \Stripe\StripeClient(STRIPE_KEY);
            $customer = $stripe->tokens->create([
                'card' => [
                    'number' => $number,
                    'exp_month' => $exp_month,
                    'exp_year' => $exp_year,
                    'cvc' => $cvc,
                    'name' => $name
                ],
            ]);
            $output['code'] = 200;
            $output['msg'] = $customer->jsonSerialize();
            return $output;
        } catch (\Stripe\Exception\ApiErrorException $e) {
            $error = self::access_protected_object($e, "error");
            $output['code'] = 201;
            $output['msg'] = $error['message'];
            return $output;
        }
    }
    public static function createCustomer(string $email, string $address1, string $address2, string $city, string $state, string $country, string $name, string $postal_code, string $token): array {
        try {
            $stripe = new \Stripe\StripeClient(STRIPE_KEY);
            $customer = $stripe->customers->create([
                'description' => '',
                'email' => $email,
                'payment_method' => $token,
                "address" => [
                    "line1" => $address1,
                    "line2" => $address2,
                    "city" => $city,
                    "country" => $country,
                    "state" => $state,
                    "postal_code" => $postal_code,
                ],
                'name' => $name,
            ]);
            $output['code'] = 200;
            $output['msg'] = $customer->jsonSerialize();
            return $output;
        } catch (\Stripe\Exception\ApiErrorException $e) {
            $error = self::access_protected_object($e, "error");
            $output['code'] = 201;
            $output['msg'] = $error['message'];
            return $output;
        }
    }
    public static function checkIfCustomerExist(string $customerid): array {
        try {
            $stripe = new \Stripe\StripeClient(STRIPE_KEY);
            $customer = $stripe->customers->retrieve($customerid, []);
            $output['code'] = 200;
            $output['msg'] = $customer->jsonSerialize();
            return $output;
        } catch (\Stripe\Exception\ApiErrorException $e) {
            $error = self::access_protected_object($e, "error");
            $output['code'] = 201;
            $output['msg'] = $error['message'];
            return $output;
        }
    }
    public static function addPaymentMethod(array $post_data): array {
        try {
            $stripe = new \Stripe\StripeClient(STRIPE_KEY);
            $customer = $stripe->paymentMethods->create([
                'type' => 'card',
                'card' => [
                    'number' => $post_data['card_number'],
                    'exp_month' => $post_data['expiration_month'],
                    'exp_year' => $post_data['expiration_year'],
                    'cvc' => $post_data['cvc'],
                ],
            ]);
            $output['code'] = 200;
            $output['msg'] = $customer->jsonSerialize();
            return $output;
        } catch (\Stripe\Exception\CardException $e) {
            $error = self::access_protected_object($e, "error");
            $output['code'] = 201;
            $output['msg'] = $error['message'];
            return $output;
        }
    }
    public function getPaymentMethod(string $pm): array {
        try {
            $stripe = new \Stripe\StripeClient(STRIPE_KEY);
            $details = $stripe->paymentMethods->retrieve($pm, []);
            $output['code'] = 200;
            $output['msg'] = $details->jsonSerialize();
            return $output;
        } catch (\Stripe\Exception\ApiErrorException $e) {
            $error = self::access_protected_object($e, "error");
            $output['code'] = 201;
            $output['msg'] = $error['message'];
            return $output;
        }
    }
    public static function generateRandomString(int $length = 8): string {
        $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        $randomString = '';
        for ($i = 0; $i < $length; $i++) {
            $randomString .= $characters[rand(0, strlen($characters) - 1)];
        }
        return $randomString;
    }
    public static function generateRandomEmail(): string {
        $domains = ["gmail.com", "yahoo.com", "hotmail.com", "example.com", "test.com"];
        $username_length = rand(5, 10);
        $domain = $domains[array_rand($domains)];
        return strtolower(self::generateRandomString($username_length) . "@" . $domain);
    }
    public static function searchProduct(string $lat, string $long): array {
        $token = MEALME_HEADER_TOKEN;
        $url = "https://api.mealme.ai/search/product/v4?user_latitude=$lat&user_longitude=$long&pickup=false&fuzzy_search=false&autocomplete=true";
        $headers = ["Id-Token:$token"];
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_PROXYPORT, 3128);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        $response = curl_exec($ch);
        curl_close($ch);
        $output_array = json_decode($response, true);
        return $output_array;
    }
    public static function verifyPhoto(string $url): array {
        $curl = curl_init();
        $payload = [
            'api_key' => DEEPENGIN_KEY,
            'url' => $url
        ];
        $curl = curl_init();
        curl_setopt_array($curl, [
            CURLOPT_URL => 'https://images.deepengin.com/v1/imageModeration',
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => '',
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 0,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => 'POST',
            CURLOPT_POSTFIELDS => json_encode($payload),
        ]);
        $response = curl_exec($curl);
        curl_close($curl);
        return json_decode($response, true);
    }
    public static function compressImage(string $image_url, string $folder_url): string {
        $new = file_get_contents($image_url);
        $fileName = uniqid();
        [$width_orig, $height_orig] = getimagesizefromstring($new);
        $width = 120;
        $aspectRatio = $height_orig / $width_orig;
        $height = intval($aspectRatio * $width);
        $image_p = imagecreatetruecolor($width, $height);
        $image = imagecreatefromstring(file_get_contents($image_url));
        imagecopyresampled($image_p, $image, 0, 0, 0, 0, $width, $height, $width_orig, $height_orig);
        imagejpeg($image_p, $folder_url, 100);
        return $folder_url;
    }
    public static function uploadOriginalVideoFileIntoTemporaryFolder(string $param): array {
        $error = 0;
        if (!array_key_exists('video', $_FILES)) {
            $msg = "Missing parameters:video";
            $error = 1;
        } else if (strlen($_FILES['video']['name']) < 1) {
            $msg = "no video is in the video paramater exist";
            $error = 1;
        }
        if ($error > 0) {
            $output['msg'] = $msg;
            $output['error'] = 1;
            return $output;
        }
        $fileName = uniqid();
        $folder = TEMP_UPLOADS_FOLDER_URI;
        if (!file_exists($folder)) {
            mkdir($folder, 0777, true);
        }
        if ($param == "image") {
            $ext = ".png";
        } else if ($param == "video") {
            $ext = ".mp4";
        } else if ($param == "audio") {
            $ext = ".mp3";
        } else if ($param == "json") {
            $ext = ".json";
        }
        $filePath = $folder . "/" . $fileName . $ext;
        if (move_uploaded_file($_FILES[$param]['tmp_name'], $filePath)) {
            $output['msg'] = $filePath;
            $output['error'] = 0;
            return $output;
        } else {
            $output['msg'] = "something went wrong in upload file";
            $output['error'] = 1;
            return $output;
        }
    }
    public static function getGoogleUserInfo(string $access_token): bool {
        if (strlen($access_token) > 500) {
            $CLIENT_ID = GOOGLE_CLIENT_ID;
            $client = new Google_Client(['client_id' => $CLIENT_ID]);
            $payload = $client->verifyIdToken($access_token);
            if ($payload) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
    public static function getFacebookUserInfo(string $access_token): bool {
        $facebook = new \Facebook\Facebook([
            'app_id' => FACEBOOK_APP_ID,
            'app_secret' => FACEBOOK_APP_SECRET,
            'default_graph_version' => FACEBOOK_GRAPH_VERSION
        ]);
        $access_token = $access_token;
        try {
            $response = $facebook->get('/me', $access_token);
        } catch (Facebook\Exceptions\FacebookResponseException $e) {
            return false;
        } catch (Facebook\Exceptions\FacebookSDKException $e) {
            return false;
        }
        return true;
    }
    public static function random_string(int $length): string {
        $key = '';
        $keys = array_merge(range(0, 9), range('a', 'z'));
        for ($i = 0; $i < $length; $i++) {
            $key .= $keys[array_rand($keys)];
        }
        return $key;
    }
    public static function unlinkFile(string $file_path): bool {
        if (file_exists($file_path)) {
            unlink($file_path);
        }
        return true;
    }
    public static function ip_info(?string $ip = null, string $purpose = "location", bool $deep_detect = true): mixed {
        $output = null;
        if (filter_var($ip, FILTER_VALIDATE_IP) === false) {
            $ip = $_SERVER["REMOTE_ADDR"];
            if ($deep_detect) {
                if (filter_var(@$_SERVER['HTTP_X_FORWARDED_FOR'], FILTER_VALIDATE_IP))
                    $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
                if (filter_var(@$_SERVER['HTTP_CLIENT_IP'], FILTER_VALIDATE_IP))
                    $ip = $_SERVER['HTTP_CLIENT_IP'];
            }
        }
        $purpose = str_replace(["name", "\n", "\t", " ", "-", "_"], "", strtolower(trim($purpose)));
        $support = ["country", "countrycode", "state", "region", "city", "location", "address"];
        $continents = [
            "AF" => "Africa",
            "AN" => "Antarctica",
            "AS" => "Asia",
            "EU" => "Europe",
            "OC" => "Australia (Oceania)",
            "NA" => "North America",
            "SA" => "South America"
        ];
        if (filter_var($ip, FILTER_VALIDATE_IP) && in_array($purpose, $support)) {
            $ipdat = @json_decode(file_get_contents("http://www.geoplugin.net/json.gp?ip=" . $ip));
            if (@strlen(trim($ipdat->geoplugin_countryCode)) == 2) {
                switch ($purpose) {
                    case "location":
                        $output = [
                            "city" => @$ipdat->geoplugin_city,
                            "state" => @$ipdat->geoplugin_regionName,
                            "country" => @$ipdat->geoplugin_countryName,
                            "country_code" => @$ipdat->geoplugin_countryCode,
                            "continent" => @$continents[strtoupper($ipdat->geoplugin_continentCode)],
                            "continent_code" => @$ipdat->geoplugin_continentCode
                        ];
                        break;
                    case "address":
                        $address = [ $ipdat->geoplugin_countryName ];
                        if (@strlen($ipdat->geoplugin_regionName) >= 1)
                            $address[] = $ipdat->geoplugin_regionName;
                        if (@strlen($ipdat->geoplugin_city) >= 1)
                            $address[] = $ipdat->geoplugin_city;
                        $output = implode(", ", array_reverse($address));
                        break;
                    case "city":
                        $output = @$ipdat->geoplugin_city;
                        break;
                    case "state":
                        $output = @$ipdat->geoplugin_regionName;
                        break;
                    case "region":
                        $output = @$ipdat->geoplugin_regionName;
                        break;
                    case "country":
                        $output = @$ipdat->geoplugin_countryName;
                        break;
                    case "countrycode":
                        $output = @$ipdat->geoplugin_countryCode;
                        break;
                }
            }
        }
        return $output;
    }
    public static function apache_request_headers(): array {
        $arh = [];
        $rx_http = '/\AHTTP_/';
        foreach ($_SERVER as $key => $val) {
            if (preg_match($rx_http, $key)) {
                $arh_key = preg_replace($rx_http, '', $key);
                $rx_matches = explode('_', $arh_key);
                if (count($rx_matches) > 0 && strlen($arh_key) > 2) {
                    foreach ($rx_matches as $ak_key => $ak_val) $rx_matches[$ak_key] = ucfirst($ak_val);
                    $arh_key = implode('-', $rx_matches);
                }
                $arh[$arh_key] = $val;
            }
        }
        return $arh;
    }
    public static function get_hashtags(string $body): array {
        $hashtag_set = [];
        $array = explode('#', $body);
        foreach ($array as $key => $row) {
            if (!empty($row)) {
                $hashtag = explode(' ', $row);
                $hashtag_set[] = $hashtag[0];
            }
        }
        return $hashtag_set;
    }
    public static function generateSessionToken(): string {
        $token = base64_encode(random_bytes(64));
        $token = strtr($token, '+/', '-_');
        return $token;
    }
    public static function compressVideo(string $video_url, string $video_id): mixed {
        $video_moderation_url = DEEPENGIN_VIDEO_COMPRESSOR_URL;
        $key = DEEPENGIN_KEY;
        $data = json_encode([
            'id' => $video_id,
            'url' => $video_url,
            'api_key' => $key,
            'webhook' => BASE_URL . "api/videoCompressorUpdate"
        ]);
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $video_moderation_url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'Content-Length: ' . strlen($data)
        ]);
        curl_setopt($ch, CURLOPT_NOSIGNAL, 1);
        $output = curl_exec($ch);
        if (curl_errno($ch)) {
            $error_msg = curl_error($ch);
            error_log("cURL error: " . $error_msg);
        }
        curl_close($ch);
        return $output;
    }
    public static function fileDownloadFromUrl(string $file_url, string $folder_url, string $ext): string {
        $random_string = self::random_string(5);
        $file_name = uniqid() . $random_string . "." . $ext;
        $result_file_path = $folder_url . "/" . $file_name;
        file_put_contents($result_file_path, fopen($file_url, 'r'));
        return $result_file_path;
    }
    public static function checkNudity(string $video_url, string $video_id): void {
        $video_moderation_url = DEEPENGIN_VIDEO_MODERATION_URL;
        $key = DEEPENGIN_KEY;
        $data = json_encode([
            'id' => $video_id,
            'url' => $video_url,
            'api_key' => $key,
            'webhook' => BASE_URL . "api/getVideoDetection"
        ]);
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $video_moderation_url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'Content-Length: ' . strlen($data)
        ]);
        curl_setopt($ch, CURLOPT_NOSIGNAL, 1);
        $output = curl_exec($ch);
        if (curl_errno($ch)) {
            $error_msg = curl_error($ch);
            error_log("cURL error: " . $error_msg);
        }
        curl_close($ch);
    }
    public static function convert_from_latin1_to_utf8_recursively(mixed $dat): mixed {
        if (is_string($dat)) {
            return utf8_encode($dat);
        } elseif (is_array($dat)) {
            $ret = [];
            foreach ($dat as $i => $d) $ret[$i] = self::convert_from_latin1_to_utf8_recursively($d);
            return $ret;
        } elseif (is_object($dat)) {
            foreach ($dat as $i => $d) $dat->$i = self::convert_from_latin1_to_utf8_recursively($d);
            return $dat;
        } else {
            return $dat;
        }
    }
    public static function resize_image(string $file, int $w, int $h, string $new_file_path, bool $crop = false): string|false {
        $info = pathinfo($file);
        if ($info["extension"] == "jpg" || $info["extension"] == "png" || $info["extension"] == "jpeg") {
            [$width, $height] = @getimagesize($file);
            $r = $width / $height;
            if ($crop) {
                if ($width > $height) {
                    $width = (int) ceil($width - ($width * abs($r - $w / $h)));
                } else {
                    $height = (int) ceil($height - ($height * abs($r - $w / $h)));
                }
                $newwidth = $w;
                $newheight = $h;
            } else {
                if ($w / $h > $r) {
                    $newwidth = $h * $r;
                    $newheight = $h;
                } else {
                    $newheight = $w / $r;
                    $newwidth = $w;
                }
            }
            if ($info["extension"] == "jpg" || $info["extension"] == "jpeg") {
                $src = imagecreatefromjpeg($file);
            } else {
                $src = imagecreatefrompng($file);
            }
            $dst = imagecreatetruecolor($newwidth, $newheight);
            imagecopyresampled($dst, $src, 0, 0, 0, 0, $newwidth, $newheight, $width, $height);
            imagejpeg($dst, $new_file_path);
            return $new_file_path;
        } else {
            return false;
        }
    }
    public static function getDurationTimeBetweenTwoDistances(string $lat1, string $long1, string $lat2, string $long2): array|false {
        $url = "https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=" . $lat1 . "," . $long1 . "&destinations=" . $lat2 . "," . $long2 . "&key=" . GOOGLE_MAPS_KEY;
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_PROXYPORT, 3128);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        $response = curl_exec($ch);
        curl_close($ch);
        $output_array = json_decode($response, true);
        if (!is_array($output_array)) {
            return false;
        }
        if (array_key_exists('error_message', $output_array)) {
            return false;
        }
        if ($output_array['rows'][0]['elements'][0]['status'] == "ZERO_RESULTS" || $output_array['rows'][0]['elements'][0]['status'] == "NOT_FOUND") {
            return false;
        } else {
            return $output_array;
        }
    }
    public static function calculateFare(float $base_fare, float $cost_per_minute, float $cost_per_distance, float $ride_duration_in_seconds, float $ride_distance_in_meters, float $surge, string $distance_unit): array {
        $ride_duration_in_minute = $ride_duration_in_seconds / 60;
        $ride_distance_in_miles = $ride_distance_in_meters * 0.00062137;
        $ride_distance_in_km = $ride_distance_in_meters / 100;
        if ($distance_unit == "M") {
            $fare = $base_fare + ($cost_per_minute * $ride_duration_in_minute) + ($cost_per_distance * $ride_distance_in_miles);
        } else if ($distance_unit == "K") {
            $fare = $base_fare + ($cost_per_minute * $ride_duration_in_minute) + ($cost_per_distance * $ride_distance_in_km);
        }
        $estimated['fare'] = round($fare, 1);
        $estimated['time'] = round($ride_duration_in_minute);
        return $estimated;
    }
    public static function checkWordsAndPerformAction(string $string, array $wordsToCheck): bool {
        $allWordsFound = true;
        foreach ($wordsToCheck as $word) {
            if (strpos($string, $word) === false) {
                $allWordsFound = false;
                break;
            }
        }
        return $allWordsFound;
    }
    public static function logRequest(string $endpoint, $jsonData): void {
        $logFile = API_LOG_FILE;
        $dateTime = date('Y-m-d H:i:s');
        $logEntry = "[$dateTime] JSON Data: Endpoint: $endpoint\n";
        $logEntry .= "[$dateTime] Endpoint: " . json_encode($jsonData, JSON_PRETTY_PRINT) . "\n";
        $logEntry .= "----------------------------------------\n";
        if (file_exists($logFile)) {
            $existingContent = file_get_contents($logFile);
        } else {
            $existingContent = '';
        }
        $newContent = $logEntry . $existingContent;
        file_put_contents($logFile, $newContent);
    }
    public static function uploadFileintoFolder(string|int $user_id, array $data, string $folder_url): string {
        $fileName = uniqid();
        $file = base64_decode($data['file_data']);
        $folder = $folder_url . '/images';
        if (!file_exists($folder)) {
            mkdir($folder, 0777, true);
        }
        $filePath = $folder . "/" . $fileName . '.png';
        file_put_contents($filePath, $file);
        return $filePath;
    }
    public function ifWordFound(string $word): bool {
        $keywords = ["nudity", "suggestive"];
        $found = false;
        foreach ($keywords as $keyword) {
            if (strpos($word, $keyword) !== false) {
                $found = true;
            }
        }
        return $found;
    }
    public static function uploadFileintoFolderDir(string $data, string $folder_url, ?string $extension = null): string {
        if ($extension === "mp4") {
            return self::uploadOriginalVideoFileIntoTemporaryFolder("video")['msg'];
        }
        $fileName = uniqid();
        $file = base64_decode($data);
        $folder = $folder_url;
        if (!file_exists($folder)) {
            mkdir($folder, 0777, true);
        }
        if (is_null($extension)) {
            $filePath = $folder . "/" . $fileName . '.jpeg';
        } else {
            $filePath = $folder . "/" . $fileName . '.' . $extension;
        }
        file_put_contents($filePath, $file);
        return $filePath;
    }
    public static function getToken(int $length): string {
        $token = "";
        $codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        for ($i = 0; $i < $length; $i++) {
            $token .= $codeAlphabet[self::crypto_rand_secure(0, strlen($codeAlphabet))];
        }
        return $token;
    }
    public static function crypto_rand_secure(int $min, int $max): int {
        $range = $max - $min;
        if ($range < 0)
            return $min;
        $log = log($range, 2);
        $bytes = (int) ($log / 8) + 1;
        $bits = (int) $log + 1;
        $filter = (int) (1 << $bits) - 1;
        do {
            $rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
            $rnd = $rnd & $filter;
        } while ($rnd >= $range);
        return $min + $rnd;
    }
    public static function randomNumber(int $length): string {
        $result = '';
        for ($i = 0; $i < $length; $i++) {
            $result .= mt_rand(0, 9);
        }
        return $result;
    }
    public function UnderscoreExist(string $string): bool {
        if (preg_match('/^[a-z]+_[a-z]+$/i', $string)) {
            return true;
        } else {
            return false;
        }
    }
    public function getValueBeforeUnderscore(string $string): string|false {
        $final_string = strstr($string, '_', true);
        if (strlen($final_string) > 0) {
            return $final_string;
        } else {
            return false;
        }
    }
    public function getValueAfterUnderscore(string $string): string|false {
        if ($this->UnderscoreExist($string)) {
            $final_string = substr($string, strpos($string, "_") + 1);
            if (strlen($final_string) > 0) {
                return $final_string;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
    public static function sendPushNotificationToMobileDevice(string $data): string {
        $project_id = FIRESTORE_PROJECT_ID;
        $url = "https://fcm.googleapis.com/v1/projects/$project_id/messages:send";
        $key = FIREBASE_PUSH_NOTIFICATION_KEY;
        $curl = curl_init();
        curl_setopt_array($curl, [
            CURLOPT_URL => "https://fcm.googleapis.com/fcm/send",
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => "",
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => "POST",
            CURLOPT_POSTFIELDS => $data,
            CURLOPT_HTTPHEADER => [
                "authorization: key=" . $key,
                "cache-control: no-cache",
                "content-type: application/json",
            ],
        ]);
        $response = curl_exec($curl);
        $err = curl_error($curl);
        curl_close($curl);
        if ($err) {
            return "cURL Error #:" . $err;
        } else {
            return $response;
        }
    }
    public static function removeMainUrl(string $url, string $before_string): string|false {
        $parsed_url = parse_url($url);
        if (isset($parsed_url['host']) && !empty($parsed_url['host'])) {
            $parts = explode('.com', $parsed_url['host']);
            $dataAfterCom = end($parts);
            return $dataAfterCom;
        } else {
            return false;
        }
    }
    public static function cropImage(string $inputImage): string {
        $file_name = uniqid();
        $ext = pathinfo($inputImage, PATHINFO_EXTENSION);
        if ($ext == "png") {
            $sourceImage = imagecreatefrompng($inputImage);
        } else {
            $sourceImage = imagecreatefromjpeg($inputImage);
        }
        $sourceWidth = imagesx($sourceImage);
        $sourceHeight = imagesy($sourceImage);
        $newHeight = 300;
        $newWidth = ($sourceWidth / $sourceHeight) * $newHeight;
        $destinationImage = imagecreatetruecolor($newWidth, $newHeight);
        imagecopyresampled($destinationImage, $sourceImage, 0, 0, 0, 0, $newWidth, $newHeight, $sourceWidth, $sourceHeight);
        $folder = TEMP_UPLOADS_FOLDER_URI . "/images/";
        if (!file_exists($folder)) {
            mkdir($folder, 0777, true);
        }
        $outputImage = $folder . $file_name . "." . $ext;
        if ($ext == "png") {
            imagepng($destinationImage, $outputImage);
        } else {
            imagejpeg($destinationImage, $outputImage);
        }
        imagedestroy($sourceImage);
        imagedestroy($destinationImage);
        return $outputImage;
    }
    public static function sendSmsVerificationCurl(string $to_number, string $msg): array {
        $id = TWILIO_ACCOUNTSID;
        $token = TWILIO_AUTHTOKEN;
        $url = "https://api.twilio.com/2010-04-01/Accounts/$id/Messages.json";
        $from = TWILIO_NUMBER;
        $to = $to_number;
        $body = $msg;
        $data = [
            'From' => $from,
            'To' => $to,
            'Body' => $body,
        ];
        $post = http_build_query($data);
        $x = curl_init($url);
        curl_setopt($x, CURLOPT_POST, true);
        curl_setopt($x, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($x, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($x, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
        curl_setopt($x, CURLOPT_USERPWD, "$id:$token");
        curl_setopt($x, CURLOPT_POSTFIELDS, http_build_query($data));
        $y = curl_exec($x);
        curl_close($x);
        return json_decode($y, true);
    }
    public static function getAge(string $dob): int {
        $today = new DateTime();
        $birthdate = new DateTime($dob);
        $interval = $today->diff($birthdate);
        $age = (int)$interval->format('%y');
        return $age;
    }
	public static function sendSmsVerification(string $to_number, string $msg): array
	{
		$settingsTable = \Cake\ORM\TableRegistry::getTableLocator()->get('Settings');
		$phoneVerify = $settingsTable->getActiveAgainstCategory('phone_verify', 1);
		if (count($phoneVerify) > 2) {
			$handler = new self();
			$companyName     = $handler->getValueBeforeUnderscore($phoneVerify[0]['Setting']['type']);
			$unknownType1    = $handler->getValueAfterUnderscore($phoneVerify[0]['Setting']['type']);
			$unknownType2    = $handler->getValueAfterUnderscore($phoneVerify[1]['Setting']['type']);
			$unknownType3    = $handler->getValueAfterUnderscore($phoneVerify[2]['Setting']['type']);
			$unknownSource1  = $phoneVerify[0]['Setting']['source'];
			$unknownSource2  = $phoneVerify[1]['Setting']['source'];
			$unknownSource3  = $phoneVerify[2]['Setting']['source'];
			if ($unknownType1 && $unknownType2 && $unknownType3) {
				$key = $secret = $fromNumber = '';
				if ($unknownType1 === 'key') {
					$key = $unknownSource1;
				} elseif ($unknownType2 === 'key') {
					$key = $unknownSource2;
				} elseif ($unknownType3 === 'key') {
					$key = $unknownSource3;
				}
				if ($unknownType1 === 'secret') {
					$secret = $unknownSource1;
				} elseif ($unknownType2 === 'secret') {
					$secret = $unknownSource2;
				} elseif ($unknownType3 === 'secret') {
					$secret = $unknownSource3;
				}
				if ($unknownType1 === 'number') {
					$fromNumber = $unknownSource1;
				} elseif ($unknownType2 === 'number') {
					$fromNumber = $unknownSource2;
				} elseif ($unknownType3 === 'number') {
					$fromNumber = $unknownSource3;
				}
				if (in_array($companyName, [NEXMO, TWILIO], true)) {
					if ($companyName === NEXMO) {
						$url = NEXMO_URL;
						$data = [
							'api_key'    => $key,
							'api_secret' => $secret,
							'to'         => $to_number,
							'from'       => $fromNumber,
							'text'       => $msg
						];
					} else {
						$url = TWILIO_URL . $key . '/SMS/Messages.json';
						$data = [
							'From'      => $fromNumber,
							'To'        => $to_number,
							'Body'      => $msg,
							'accountid' => $key,
							'token'     => $secret
						];
					}
					$post = http_build_query($data);
					$ch = curl_init($url);
					curl_setopt($ch, CURLOPT_POST, true);
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
					curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
					if ($companyName === TWILIO) {
						curl_setopt($ch, CURLOPT_USERPWD, "$key:$secret");
					}
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
					$response = curl_exec($ch);
					curl_close($ch);
					$result = json_decode($response, true);
					if ($companyName === NEXMO) {
						if (!empty($result['messages'][0]['error-text'])) {
							return ['code' => 203, 'msg' => $result['messages'][0]['error-text'], 'msg_from_company' => $result, 'data' => $data];
						}
						if (isset($result['messages'][0]['status'])) {
							if ($result['messages'][0]['status'] === '0') {
								return ['code' => 200, 'msg' => $result['messages'][0]['error-text'] ?? '', 'msg_from_company' => $result, 'data' => $data];
							}
							return ['code' => 203, 'msg' => $result['messages'][0]['status'], 'msg_from_company' => $result, 'data' => $data];
						}
						return ['code' => 203, 'msg' => UNKNOWN_ERROR, 'msg_from_company' => $result, 'data' => $data];
					}
					if ($companyName === TWILIO && isset($result['code']) && in_array($result['code'], [21608, 201, 21606, 20003], true)) {
						return ['code' => 203, 'msg' => $result['message'] ?? '', 'msg_from_company' => $result, 'data' => $data];
					}
				}
				return ['code' => 201, 'msg' => 'Company is invalid'];
			}
			return ['code' => 201, 'msg' => 'underscore is missing in the database value'];
		}
		return ['code' => 201, 'msg' => 'Something is missing in the database. There should be three values in the database(key,secret,number)'];
	}
    public static function getCloudFrontUrl(string $url, string $before_string): string {
        $aws = strpos($url, 'amazonaws');
        if ($aws) {
            $parsed_url = parse_url($url);
            if (strpos($parsed_url['host'], '.com') !== false) {
                $com_url = $parsed_url['scheme'] . '://' . $parsed_url['host'];
                $string = strpos($url, 'tictic-video');
                if ($string) {
                    $url = str_replace("/", "", $url);
                }
                $url = str_replace($com_url, CLOUDFRONT_URL, $url);
            }
            return $url;
        } else {
            return $url;
        }
    }
    public static function sendMail(array $data): array {
        $mail = new PHPMailer(true);
        try {
            $mail->isSMTP();
            $mail->Host = MAIL_HOST;
            $mail->SMTPAuth = true;
            $mail->Username = MAIL_USERNAME;
            $mail->Password = MAIL_PASSWORD;
            $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
            $mail->Port = 587;
            $mail->setFrom(MAIL_FROM, MAIL_NAME);
            $mail->addAddress($data['to'], $data['name']);
            $mail->addReplyTo(MAIL_REPLYTO);
            $mail->isHTML(true);
            $mail->Subject = $data['subject'];
            $mail->Body = $data['message'];
            $mail->send();
            $array['code'] = 200;
            $array['msg'] = "success";
            return $array;
        } catch (Exception $e) {
            $array['code'] = 201;
            $array['msg'] = $mail->ErrorInfo;
            return $array;
        }
    }
}
?>
