<?php

namespace Phox\core\admin\panel\wdes_options\pages\elements\actions;

if ( ! defined( 'ABSPATH' ) ) {
    exit; //Exit if assessed directly
}

use Phox\core\admin\panel\wdes_options\pages\elements\actions\Abstract_Action;

/**
 * Fetch_Whmcs_Data class handles fetching product data from a WHMCS instance,
 * validating the input data, making API requests, and saving the fetched data.
 */
class Fetch_Whmcs_Data extends Abstract_Action
{

    /**
     * Fetches data from WHMCS based on provided URL, identifier, and secret.
     *
     * This method validates the provided WHMCS credentials, makes an API call to fetch
     * the products, saves the fetched data, and returns a success message upon completion.
     *
     * @return false|void Returns false if validation, API call, or data saving fails, otherwise sends a JSON success response.
     */
    public function fetch_whmcs_data () {

        //check if url , id , secret has been added
        $url = $_POST['whmcs_url'];
        $id = $_POST['whmcs_identifier'];
        $secret = $_POST['whmcs_secret'];

        if( ! $this->button_action_check_validation( $url, $id,  $secret) ) return false;

        $args = [
            'identifier' => $id,
            'secret' => $secret,
            'action' => 'GetProducts',
            'responsetype' => 'json',
        ];

        $response = $this->button_action_whmcs_api_call($url, $args);

        if( ! $response ) return false;

        $save_status = $this->button_action_save_data( $response );

        if( ! $save_status ) return false;

        wp_send_json_success( ['type' => 'success save', 'message' => $this->button_action_prepare_message('Connecting with WHMCS and fetching data has been completed successfully.')] );

    }

    /**
     * Validates the provided WHMCS credentials including URL, identifier, and secret.
     *
     * This method checks if the URL, ID, and secret fields are non-empty and valid.
     * If validation fails at any step, an error message is sent back and the method returns false.
     *
     * @param string $url The WHMCS URL to be validated.
     * @param string $id The WHMCS identifier to be validated.
     * @param string $secret The WHMCS secret to be validated.
     * @return bool Returns true if all validations pass, otherwise false.
     */
    private function button_action_check_validation($url, $id, $secret ): bool
    {

        if( empty($url) &&  empty($id) && empty($secret)) {

            wp_send_json_error( ['type' => 'error validation', 'message' => $this->button_action_prepare_message('You must enter data on (URL, ID, Secret) fields and save them before connecting with your WHMCS site.')]);

            return false;

        }

        if( ! filter_var($url, FILTER_VALIDATE_URL) ){

            wp_send_json_error( ['type' => 'error validation', 'message' => $this->button_action_prepare_message('Please enter valid url')]);

            return false;
        }

        if( strlen( $id ) < 20 && strlen( $secret ) < 20 ){

            wp_send_json_error( ['type' => 'error validation', 'message' => $this->button_action_prepare_message('Please enter valid ID & Secret')]);

            return false;

        }

        return true;

    }

    /**
     * Executes an API call to the WHMCS system with the given URL and arguments.
     *
     * This method constructs the API URL, prepares the request data, and sends a
     * POST request using `wp_remote_post()`. If the request is successful and the
     * response code is 200, it returns the parsed JSON response body. In case of an
     * error, it sends a JSON error response and returns false.
     *
     * @param string $url The base URL of the WHMCS installation.
     * @param array $args Optional. An associative array of parameters to be sent in the API request.
     * @return mixed Returns an associative array parsed from the JSON response body on success, or false on failure.
     */
    private function button_action_whmcs_api_call($url, $args = [] ):mixed
    {

        $api_url = esc_url(  $url . '/includes/api.php'  );

        $data = http_build_query( $args );

        $request_args = [
            'timeout'     => 30,
            'body'        => $data,
        ];

        $request = wp_remote_post( $api_url, $request_args );

        if( is_wp_error($request) ){

            wp_send_json_error( ['type' => 'error connection', 'message' => $request->get_error_message()]);

            return false;
        }

        $response_code = wp_remote_retrieve_response_code( $request );

        $response_body = wp_remote_retrieve_body( $request );

        if( $response_code !== 200 ){

            $message = explode('Authentication', $response_body);

            if( isset( $message[1] ) ){

                $message  = 'Authentication Failed';

            }else{
                $message  = json_decode($message[0], true)['message'];
            }

            wp_send_json_error( ['type' => 'error connection', 'message' => $message]);

            return false;
        }

        return json_decode($response_body, true);

    }

    /**
     * Saves the fetched WHMCS data to the WordPress database.
     *
     * This method processes the provided data from WHMCS, validates it, and saves it as a WordPress
     * option. It also updates related posts and meta values if required. In case of any errors during
     * the data processing or validation, it sends a JSON error response and halts the execution.
     *
     * @param array $data The data fetched from WHMCS that needs to be processed and saved.
     * @return bool Returns false if there are validation errors or processing issues, otherwise returns true upon successful operation.
     */
    private function button_action_save_data($data):bool
    {

        $option_value = [];

        $uids = [];

        if( $data['result'] !== 'success' && $data['totalresults'] < 0 ) {

            wp_send_json_error( ['type' => 'error after fetch data', 'message' => $this->button_action_prepare_message('There are no products')]);

            return false;
        }

        $option = get_option( self::WHMCS_FETCH_DATA_OPTION_NAME, '' );

        if( ! empty( $option ) ){

            wp_send_json_error( ['type' => 'error found option', 'message' => $this->button_action_prepare_message('You need to remove old data first before fetch a new data')]);

            return false;

        }

        if(empty($data['products']['product'])){
            wp_send_json_error( ['type' => 'error import', 'message' => $this->button_action_prepare_message('You have not any products to import from your WHMCS site.')]);

            return false;
        }

        foreach ( $data['products']['product'] as $product ){

            $uid = uniqid( 'wdes'.$product['pid'].'_' );

            unset($product['customfields']);
            unset($product['configoptions']);

            $uids['wdes'.$product['pid']] = $uid;

            $option_value ['products'][$uid] = $product;

        }


        add_option( self::WHMCS_FETCH_DATA_OPTION_NAME, $option_value );

        $get_posts = $this->button_sql_get_posts();

        if( $get_posts ){

            $update_sql_data = $this->button_sql_change_setting($get_posts, $uids);

            if( ! empty( $update_sql_data ) ){

                $this->button_sql_update_meta_value($update_sql_data);

            }

        }

        return true;

    }

    /**
     * Retrieves posts from the database that match a specific meta value.
     *
     * This method fetches posts from the WordPress database where the meta value
     * contains a specific search word and the post status is not 'inherit'.
     *
     * @return array|false Returns an array of results if any matches are found, otherwise returns false.
     */
    private function button_sql_get_posts()
    {

        global $wpdb;

        $posts_elementor_settings = [];

        $search_word = '%whmcs_settings_select_product%';
        $condition_status = 'inherit';

        $sql  = $wpdb->prepare( "SELECT * FROM $wpdb->postmeta where meta_value like %s and post_id IN (Select id from $wpdb->posts where post_status != %s)", $search_word, $condition_status );
        $results  = $wpdb->get_results( $sql );

        return ! empty( $results ) ? $results : false;

    }

    /**
     * Updates SQL settings for specific elements based on given data.
     *
     * This method processes the provided SQL data, iterates through the elements,
     * and modifies specific settings related to WHMCS price table widgets.
     * It filters out certain settings and updates product IDs based on provided UID mappings.
     *
     * @param array $sql_data An array of SQL data objects, each containing meta values to be processed.
     * @param array $uids An associative array mapping old product IDs to new UIDs.
     * @return array Returns an array of processed SQL data with updated meta values.
     */
    private function button_sql_change_setting(array $sql_data, array $uids): array
    {

        $after_update_sql_data = [];

        foreach ( $sql_data as $post_setting ){

            $meta_value = json_decode($post_setting->meta_value, true);

            $update_meta_value = $meta_value;

            foreach ( $meta_value as $s_key => $section ){

                foreach ( $section['elements'] as $c_key => $column ){

                    foreach ( $column['elements'] as $e_key => $element ) {

                        if( $element['widgetType'] === 'wdes_whmcs_price_table' ){

                            foreach ( $element['settings'] as $se_key => $setting ){

                                if( ! is_bool( strpos($se_key, 'title_wdes') ) || ! is_bool( strpos($se_key, 'feature_items_wdes') ) || ! is_bool( strpos($se_key, 'button_link__wdes') ) ) {

                                    unset($update_meta_value[$s_key]['elements'][$c_key]['elements'][$e_key]['settings'][$se_key]);

                                }

                                if( $se_key === 'whmcs_settings_select_product' ){

                                    $product_id = preg_split("/_[a-z 0-9]+/", $setting);

                                    $update_meta_value[$s_key]['elements'][$c_key]['elements'][$e_key]['settings'][$se_key] = $uids[$product_id[0]];

                                }

                            }

                        }

                    }

                }

            }

            $after_update_sql_data [$post_setting->meta_id] = json_encode($update_meta_value);

        }

        return $after_update_sql_data;

    }

    /**
     * Updates the meta values in the database using the provided data array.
     *
     * This method takes an associative array where the keys are meta IDs and the values
     * are the new meta values to be updated. It iterates through the array and updates
     * each meta value in the 'postmeta' table.
     *
     * @param array $update_sql_data An associative array of meta IDs and their new meta values to be updated.
     * @return void
     */
    private function button_sql_update_meta_value(array $update_sql_data ):void
    {

        global $wpdb;

        foreach ( $update_sql_data as $meta_id => $meta_value ){

            $wpdb->update( $wpdb->postmeta, ['meta_value' => $meta_value], ['meta_id' => $meta_id ] );

        }


    }

    /**
     * Initiates the process to fetch data from WHMCS.
     *
     * This method triggers the `fetch_whmcs_data` function to start the process of obtaining WHMCS data.
     *
     * @return void
     */
    public function call():void
    {
        $this->fetch_whmcs_data();
    }
}