<?php

class MailChimp_WooCommerce_Rest_Api
{
    protected static $namespace = 'mailchimp-for-woocommerce/v1';

	/**
	 * @param $path
	 *
	 * @return string
	 */
    public static function url($path)
    {
        return esc_url_raw(rest_url(static::$namespace.'/'.ltrim($path, '/')));
    }
    /**
     * Register all Mailchimp API routes.
     */
    public function register_routes()
    {
        // ping
        register_rest_route(static::$namespace, '/ping', array(
            'methods' => 'GET',
            'callback' => array($this, 'ping'),
            'permission_callback' => '__return_true',
        ));

        // Right now we only have a survey disconnect endpoint.
        register_rest_route(static::$namespace, "/survey/disconnect", array(
            'methods' => 'POST',
            'callback' => array($this, 'post_disconnect_survey'),
            'permission_callback' => array($this, 'permission_callback'),
        ));

        // Sync Stats
        register_rest_route(static::$namespace, '/sync/stats', array(
            'methods' => 'GET',
            'callback' => array($this, 'get_sync_stats'),
            'permission_callback' => array($this, 'permission_callback'),
        ));

        // remove review banner
        register_rest_route(static::$namespace, "/review-banner", array(
            'methods' => 'GET',
            'callback' => array($this, 'dismiss_review_banner'),
            'permission_callback' => array($this, 'permission_callback'),
        ));

        //Member Sync
        register_rest_route(static::$namespace, "/member-sync", array(
            'methods' => 'GET',
            'callback' => array($this, 'member_sync_alive_signal'),
            'permission_callback' => '__return_true',
        ));
        register_rest_route(static::$namespace, "/member-sync", array(
            'methods' => 'POST',
            'callback' => array($this, 'member_sync'),
            'permission_callback' => '__return_true',
        ));

        // Tower report
        register_rest_route(static::$namespace, "/tower/report", array(
            'methods' => 'POST',
            'callback' => array($this, 'get_tower_report'),
            'permission_callback' => '__return_true',
        ));

        // tower logs
        register_rest_route(static::$namespace, "/tower/logs", array(
            'methods' => 'POST',
            'callback' => array($this, 'get_tower_logs'),
            'permission_callback' => '__return_true',
        ));

        register_rest_route(static::$namespace, "/tower/resource", array(
            'methods' => 'POST',
            'callback' => array($this, 'get_tower_resource'),
            'permission_callback' => '__return_true',
        ));

        register_rest_route(static::$namespace, "/tower/action", array(
            'methods' => 'POST',
            'callback' => array($this, 'handle_tower_action'),
            'permission_callback' => '__return_true',
        ));

        register_rest_route(static::$namespace, "/tower/sync_stats", array(
            'methods' => 'POST',
            'callback' => array($this, 'get_tower_sync_stats'),
            'permission_callback' => '__return_true',
        ));

    }

    /**
     * @return bool
     */
    public function permission_callback()
    {
        $cap = mailchimp_get_allowed_capability();
        return ($cap === 'manage_woocommerce' || $cap === 'manage_options' );
    }

	/**
	 * @param WP_REST_Request $request
	 *
	 * @return WP_REST_Response
	 */
    public function ping(WP_REST_Request $request)
    {
        return $this->mailchimp_rest_response(array('success' => true));
    }

	/**
	 * @param WP_REST_Request $request
	 *
	 * @return WP_REST_Response
	 */
    public function post_disconnect_survey(WP_REST_Request $request)
    {
        // need to send a post request to
        $host = mailchimp_environment_variables()->environment === 'staging' ?
        'https://staging.conduit.vextras.com' : 'https://conduit.mailchimpapp.com';

        $route = "{$host}/survey/woocommerce";

        $result = wp_remote_post(esc_url_raw($route), array(
            'timeout'   => 12,
            'blocking'  => true,
            'method'      => 'POST',
            'data_format' => 'body',
            'headers'     => array('Content-Type' => 'application/json; charset=utf-8'),
            'body'        => json_encode($request->get_params()),
        ));

        return $this->mailchimp_rest_response($result);
    }

	/**
	 * @param WP_REST_Request $request
	 *
	 * @return WP_REST_Response
	 */
    public function get_sync_stats(WP_REST_Request $request)
    {
        // if the queue is running in the console - we need to say tell the response why it's not going to fire this way.
        if (!mailchimp_is_configured() || !($api = mailchimp_get_api())) {
            return $this->mailchimp_rest_response(array('success' => false, 'reason' => 'not configured'));
        }

        $store_id = mailchimp_get_store_id();
        
        $complete = array(
            'coupons' => get_option('mailchimp-woocommerce-sync.coupons.completed_at'),
            'products' => get_option('mailchimp-woocommerce-sync.products.completed_at'),
            'orders' => get_option('mailchimp-woocommerce-sync.orders.completed_at')
        );

        $promo_rules_count = mailchimp_get_coupons_count();
        $product_count = mailchimp_get_product_count();
        $order_count = mailchimp_get_order_count();

        $mailchimp_total_promo_rules = $complete['coupons'] ? $promo_rules_count - mailchimp_get_remaining_jobs_count('MailChimp_WooCommerce_SingleCoupon') : 0;
        $mailchimp_total_products = $complete['products'] ? $product_count - mailchimp_get_remaining_jobs_count('MailChimp_WooCommerce_Single_Product') : 0;
        $mailchimp_total_orders = $complete['orders'] ? $order_count - mailchimp_get_remaining_jobs_count('MailChimp_WooCommerce_Single_Order') : 0;
        // try {
        //     $promo_rules = $api->getPromoRules($store_id, 1, 1, 1);
        //     $mailchimp_total_promo_rules = $promo_rules['total_items'];
        //     if (isset($promo_rules_count['publish']) && $mailchimp_total_promo_rules > $promo_rules_count['publish']) $mailchimp_total_promo_rules = $promo_rules_count['publish'];
        // } catch (Exception $e) { $mailchimp_total_promo_rules = 0; }
        // try {
        //     $products = $api->products($store_id, 1, 1);
        //     $mailchimp_total_products = $products['total_items'];
        //     if ($mailchimp_total_products > $product_count) $mailchimp_total_products = $product_count;
        // } catch (Exception $e) { $mailchimp_total_products = 0; }
        // try {
        //     $orders = $api->orders($store_id, 1, 1);
        //     $mailchimp_total_orders = $orders['total_items'];
        //     if ($mailchimp_total_orders > $order_count) $mailchimp_total_orders = $order_count;
        // } catch (Exception $e) { $mailchimp_total_orders = 0; }

        $date = mailchimp_date_local('now');

        // but we need to do it just in case.
        return $this->mailchimp_rest_response(array(
            'success' => true,
            'promo_rules_in_store' => $promo_rules_count,
            'promo_rules_in_mailchimp' => $mailchimp_total_promo_rules,
            
            'products_in_store' => $product_count,
            'products_in_mailchimp' => $mailchimp_total_products,
            
            'orders_in_store' => $order_count,
            'orders_in_mailchimp' => $mailchimp_total_orders,
            
            // 'promo_rules_page' => get_option('mailchimp-woocommerce-sync.coupons.current_page'),
            // 'products_page' => get_option('mailchimp-woocommerce-sync.products.current_page'),
            // 'orders_page' => get_option('mailchimp-woocommerce-sync.orders.current_page'),
            
            'date' => $date ? $date->format( __('D, M j, Y g:i A', 'mailchimp-for-woocommerce')) : '',
            'has_started' => mailchimp_has_started_syncing() || ($order_count != $mailchimp_total_orders),
            'has_finished' => mailchimp_is_done_syncing() && ($order_count == $mailchimp_total_orders),
	        'last_loop_at' => mailchimp_get_data('sync.last_loop_at'),
        ));
    }

	/**
	 * @param WP_REST_Request $request
	 *
	 * @return WP_REST_Response
	 */
    public function dismiss_review_banner(WP_REST_Request $request)
    {
        return $this->mailchimp_rest_response(array('success' => delete_option('mailchimp-woocommerce-sync.initial_sync')));
    }

	/**
	 * @param WP_REST_Request $request
	 *
	 * @return WP_REST_Response
	 */
    public function member_sync(WP_REST_Request $request)
    {
        $this->authorize('webhook.token', $request);
        $data = $request->get_params();
        if (!empty($data['type']) && !empty($data['data']['list_id']) && mailchimp_get_list_id() == $data['data']['list_id'] ){
            $job = new MailChimp_WooCommerce_Subscriber_Sync($data);
            $job->handle();
            return $this->mailchimp_rest_response(array('success' => true));
        }
        return $this->mailchimp_rest_response(array('success' => false));
    }

	/**
	 * Returns an alive signal to confirm url exists to mailchimp system
	 * @param WP_REST_Request $request
	 *
	 * @return WP_REST_Response
	 */
    public function member_sync_alive_signal(WP_REST_Request $request)
    {
        $this->authorize('webhook.token', $request);
        return $this->mailchimp_rest_response(array('success' => true));
    }

	/**
	 * @param WP_REST_Request $request
	 *
	 * @return WP_REST_Response
	 * @throws MailChimp_WooCommerce_Error
	 * @throws MailChimp_WooCommerce_ServerError
	 */
    public function get_tower_report(WP_REST_Request $request)
    {
        $this->authorize('tower.token', $request);
        return $this->mailchimp_rest_response(
            $this->tower($request->get_query_params())->handle()
        );
    }

	/**
	 * @param WP_REST_Request $request
	 *
	 * @return WP_REST_Response
	 * @throws MailChimp_WooCommerce_Error
	 * @throws MailChimp_WooCommerce_RateLimitError
	 * @throws MailChimp_WooCommerce_ServerError
	 * @throws Throwable
	 */
    public function handle_tower_action(WP_REST_Request $request)
    {
        $this->authorize('tower.token', $request);
        $body = $request->get_json_params();

        $action = isset($body['action']) ? $body['action'] : null;
        $data = isset($body['data']) ? $body['data'] : null;
        $response = null;

        if (empty($action)) {
            return $this->mailchimp_rest_response(array(
                'success' => false,
                'message' => 'invalid action'
            ));
        }

        switch ($action) {
            case 'emergency_stop_syncing':
                mailchimp_set_data('emergency_stop', true);
                $response = [
                    'title' => "Successfully stopped the sync.",
                    'description' => "Please note you'll need to have them reconnect.",
                    'type' => 'success',
                ];
                break;
            case 'update_feature':
                $response = [
                    'title' => "Features are not available for WooCommerce",
                    'type' => 'error',
                ];
                break;
            case 'resync_orders':
                MailChimp_WooCommerce_Process_Orders::push();
                $response = [
                    'title' => "Successfully initiated the order resync",
                    'description' => "Please note that it will take a couple minutes to start this process. Check the store logs for details.",
                    'type' => 'success',
                ];
                break;
            case 'resync_products':
                MailChimp_WooCommerce_Process_Products::push();
                $response = [
                    'title' => "Successfully initiated product resync",
                    'description' => "Please note that it will take a couple minutes to start this process. Check the store logs for details.",
                    'type' => 'success',
                ];
                break;
            case 'resync_customers':
                $response = [
                    'title' => "Customer resync",
                    'description' => "WooCommerce does not have customers to sync. Only orders.",
                    'type' => 'error',
                ];
                break;
            case 'resync_promo_codes':
                MailChimp_WooCommerce_Process_Coupons::push();
                $response = [
                    'title' => "Successfully initiated promo code resync",
                    'description' => "Please note that it will take a couple minutes to start this process. Check the store logs for details.",
                    'type' => 'success',
                ];
                break;
            case 'resync_chimpstatic_script':
                $response = [
                    'title' => "Chimpstatic script",
                    'description' => 'Scripts are automatically injected at runtime.',
                    'type' => 'error',
                ];
                break;
            case 'activate_webhooks':
                $api = mailchimp_get_api();
                $list = mailchimp_get_list_id();
                if (get_option('permalink_structure') === '') {
                    $response = [
                        'title' => "Store Webhooks",
                        'description' => "No store webhooks to apply",
                        'type' => 'error',
                    ];
                } else {
                    $previous_url = mailchimp_get_webhook_url();
                    if (mailchimp_get_data('webhook.token') && $previous_url && $api->hasWebhook($list, $previous_url)) {
                        $response = [
                            'title' => "Store Webhooks",
                            'description' => "Store already has webhooks enabled!",
                            'type' => 'success',
                        ];
                    } else {
                        $key = mailchimp_create_webhook_token();
                        $url = mailchimp_build_webhook_url($key);
                        mailchimp_set_data('webhook.token', $key);
                        try {
                            $webhook = $api->webHookSubscribe($list, $url);
                            mailchimp_set_webhook_url($webhook['url']);
                            mailchimp_log('webhooks', "added webhook to audience");
                            $response = [
                                'title' => "Store Webhooks",
                                'description' => "Set up a new webhook at {$webhook['url']}",
                                'type' => 'success',
                            ];
                        } catch (Exception $e) {
                            $response = [
                                'title' => "Store Webhooks",
                                'description' => $e->getMessage(),
                                'type' => 'error',
                            ];
                            mailchimp_set_data('webhook.token', false);
                            mailchimp_set_webhook_url(false);
                            mailchimp_error('webhook', $e->getMessage());
                        }
                    }
                }
                break;
            case 'resync_all':
                $service = new MailChimp_Service();
                $service->removePointers();
                MailChimp_WooCommerce_Admin::instance()->startSync();
                $service->setData('sync.config.resync', true);
                $response = [
                    'title' => "Successfully initiated the store resync",
                    'description' => "Please note that it will take a couple minutes to start this process. Check the store logs for details.",
                    'type' => 'success',
                ];
                break;
            case 'resync_customer':
                $response = [
                    'title' => "Error syncing custome",
                    'description' => "WooCommerce only works with orders.",
                    'type' => 'error',
                ];
                break;
            case 'resync_order':
                $order = new WC_Order($data['id']);
                if (!$order->get_date_created()) {
                    $response = [
                        'title' => "Error syncing order",
                        'description' => "This order id does not exist.",
                        'type' => 'error',
                    ];
                } else {
                    $job = new MailChimp_WooCommerce_Single_Order($order);
                    $data = $job->handle();
                    $response = [
                        'title' => "Executed order resync",
                        'description' => "Check the store logs for details.",
                        'type' => 'success',
                    ];
                }
                break;
            case 'resync_product':
                $product = new WC_Product($data['id']);
                if (!$product->get_date_created()) {
                    $response = [
                        'title' => "Error syncing product",
                        'description' => "This product id does not exist.",
                        'type' => 'error',
                    ];
                } else {
                    $job = new MailChimp_WooCommerce_Single_Product($product);
                    $data = $job->handle();
                    $response = [
                        'title' => "Executed product resync",
                        'description' => "Check the store logs for details.",
                        'type' => 'success',
                    ];
                }
                break;
            case 'resync_cart':
                $response = [
                    'title' => "Let's talk",
                    'description' => "This isn't supported by our system yet. If you really need this, please say something.",
                    'type' => 'error',
                ];
                break;
            case 'fix_duplicate_store':
                $job = new MailChimp_WooCommerce_Fix_Duplicate_Store(mailchimp_get_store_id(), true, false);
                $job->handle();
                $response = [
                    'title' => "Successfully queued up store deletion.",
                    'description' => "This process may take a couple minutes to complete. Please check back by reloading the page after a minute.",
                    'type' => 'success',
                ];
                break;
            case 'remove_legacy_app':
                $response = [
                    'title' => "Error removing legacy app",
                    'description' => "WooCommerce doesn't have any legacy apps to delete.",
                    'type' => 'error',
                ];
                break;
        }

        return $this->mailchimp_rest_response($response);
    }

	/**
	 * @param WP_REST_Request $request
	 *
	 * @return WP_REST_Response
	 */
    public function get_tower_logs(WP_REST_Request $request)
    {
        $this->authorize('tower.token', $request);
        return $this->mailchimp_rest_response(
            $this->tower($request->get_query_params())->logs()
        );
    }

	/**
	 * @param WP_REST_Request $request
	 *
	 * @return WP_REST_Response
	 * @throws MailChimp_WooCommerce_Error
	 * @throws MailChimp_WooCommerce_RateLimitError
	 * @throws MailChimp_WooCommerce_ServerError
	 */
    public function get_tower_resource(WP_REST_Request $request)
    {
        $this->authorize('tower.token', $request);
        $body = json_decode($request->get_body(), true);

        if (!isset($body['resource']) || !isset($body['resource_id'])) {
            return $this->mailchimp_rest_response(array(
                'resource' => null,
                'resource_error' => 'Resource not found because post request was wrong',
                'mailchimp' => null,
                'mailchimp_error' => 'Resource not found because post request was wrong',
            ));
        }

        $platform = null;
        $mc = null;
        $store_id = mailchimp_get_store_id();
        switch ($body['resource']) {
            case 'order':
                $order = get_post($body['resource_id']);
                $mc = !$order->ID ? null : mailchimp_get_api()->getStoreOrder($store_id, $order->ID);
                if ($order->ID) {
                    $transformer = new MailChimp_WooCommerce_Transform_Orders();
                    $platform = $transformer->transform($order)->toArray();
                }
                if ($mc) $mc = $mc->toArray();
                break;
            case 'customer':
                $body['resource_id'] = urldecode($body['resource_id']);
                $field = is_email($body['resource_id']) ? 'email' : 'id';
                $platform = get_user_by($field, $body['resource_id']);
                if ($platform) {
                    $platform->mailchimp_woocommerce_is_subscribed = (bool) get_user_meta($platform->ID, 'mailchimp_woocommerce_is_subscribed', true);
                }
                $hashed = mailchimp_hash_trim_lower($platform->user_email);
                if ($mc = mailchimp_get_api()->getCustomer($store_id, $hashed)) {
                    try {
                        $member = mailchimp_get_api()->member(mailchimp_get_list_id(), $mc->getEmailAddress());
                    } catch (Exception $e) {
                        $member = null;
                    }
                    $mc = array(
                        'customer' => $mc->toArray(),
                        'member' => $member,
                    );
                }
                break;
            case 'product':
                $platform = get_post($body['resource_id']);
                if ($platform) {
                    $transformer = new MailChimp_WooCommerce_Transform_Products();
                    $platform = $transformer->transform($platform)->toArray();
                }
                if ($mc = mailchimp_get_api()->getStoreProduct($store_id, $body['resource_id'])) {
                    $mc = $mc->toArray();
                }
                break;
            case 'cart':
                global $wpdb;
                $uid = mailchimp_hash_trim_lower($body['resource_id']);
                $table = "{$wpdb->prefix}mailchimp_carts";
                $sql = $wpdb->prepare("SELECT * FROM $table WHERE id = %s", $uid);
                $platform = $wpdb->get_row($sql);
                if ($mc = mailchimp_get_api()->getCart($store_id, $uid)) {
                    $mc = $mc->toArray();
                }
                break;
            case 'promo_code':
                $platform = new WC_Coupon($body['resource_id']);
	            $mc = mailchimp_get_api()->getPromoRuleWithCodes($store_id, $body['resource_id']);
                break;
        }

        return $this->mailchimp_rest_response(array(
            'resource' => $platform,
            'resource_error' => empty($platform) ? 'Resource not found' : false,
            'mailchimp' => $mc,
            'mailchimp_error' => empty($mc) ? 'Resource not found' : false,
        ));
    }

	/**
	 * @param WP_REST_Request $request
	 *
	 * @return WP_REST_Response
	 */
    public function get_tower_sync_stats(WP_REST_Request $request)
    {
        $this->authorize('tower.token', $request);

        // if the queue is running in the console - we need to say tell the response why it's not going to fire this way.
        if (!mailchimp_is_configured() || !($api = mailchimp_get_api())) {
            return $this->mailchimp_rest_response(array('success' => false, 'reason' => 'not configured'));
        }

        $store_id = mailchimp_get_store_id();
        $product_count = mailchimp_get_product_count();
        $order_count = mailchimp_get_order_count();

        try {
            $products = $api->products($store_id, 1, 1);
            $mailchimp_total_products = $products['total_items'];
            if ($mailchimp_total_products > $product_count) {
                $mailchimp_total_products = $product_count;
            }
        } catch (Exception $e) { $mailchimp_total_products = 0; }
        try {
            $mailchimp_total_customers = $api->getCustomerCount($store_id);
        } catch (Exception $e) { $mailchimp_total_customers = 0; }
        try {
            $orders = $api->orders($store_id, 1, 1);
            $mailchimp_total_orders = $orders['total_items'];
            if ($mailchimp_total_orders > $order_count) {
                $mailchimp_total_orders = $order_count;
            }
        } catch (Exception $e) { $mailchimp_total_orders = 0; }

        // but we need to do it just in case.
        return $this->mailchimp_rest_response(array(
            'platform' => array(
                'products' => $product_count,
                'customers' => 0,
                'orders' => $order_count,
            ),
            'mailchimp' => array(
                'products' => $mailchimp_total_products,
                'customers' => $mailchimp_total_customers,
                'orders' => $mailchimp_total_orders,
            ),
        ));
    }

	/**
	 * @param null $params
	 *
	 * @return MailChimp_WooCommerce_Tower
	 */
    private function tower($params = null)
    {
        if (!is_array($params)) $params = array();
        $job = new MailChimp_WooCommerce_Tower(mailchimp_get_store_id());
        $job->withLogFile(!empty($params['log_view']) ? $params['log_view'] : null);
        $job->withLogSearch(!empty($params['search']) ? $params['search'] : null);
        return $job;
    }

	/**
	 * @param $data
	 * @param int $status
	 *
	 * @return WP_REST_Response
	 */
    private function mailchimp_rest_response($data, $status = 200)
    {
        if (!is_array($data)) $data = array();
        $response = new WP_REST_Response($data);
        $response->set_status($status);
        return $response;
    }

	/**
	 * @param $key
	 * @param WP_REST_Request $request
	 *
	 * @return bool
	 */
    private function authorize($key, WP_REST_Request $request)
    {
        $allowed_keys = array(
            'tower.token',
            'webhook.token',
        );
        // this is just a safeguard against people trying to do wonky things.
        if (!in_array($key, $allowed_keys, true)) {
            wp_send_json_error(array('message' => 'unauthorized token type'), 403);
        }
        // get the auth token from either a header, or the query string
        $token = $this->getAuthToken($request);
        // pull the saved data
        $saved = mailchimp_get_data($key);

        // if we don't have a token - or we don't have the saved comparison
        // or the token doesn't equal the saved token, throw an error.
        if (empty($token) || empty($saved) || ($token !== $saved && base64_decode($token) !== $saved)) {
            wp_send_json_error(array('message' => 'unauthorized'), 403);
        }
        return true;
    }

	/**
	 * @param WP_REST_Request $request
	 *
	 * @return false|mixed|string
	 */
    private function getAuthToken(WP_REST_Request $request)
    {
        if (($token = $this->getBearerTokenHeader($request))) {
            return $token;
        }
        return $this->getAuthQueryStringParam($request);
    }

	/**
	 * @param WP_REST_Request $request
	 *
	 * @return false|string
	 */
    private function getBearerTokenHeader(WP_REST_Request $request)
    {
        $header = $request->get_header('Authorization');
        $position = strrpos($header, 'Bearer ');
        if ($position !== false) {
            $header = substr($header, $position + 7);
            return strpos($header, ',') !== false ?
                strstr(',', $header, true) :
                $header;
        }
        return false;
    }

	/**
	 * @param WP_REST_Request $request
	 *
	 * @return false|mixed
	 */
    private function getAuthQueryStringParam(WP_REST_Request $request)
    {
        $params = $request->get_query_params();
        return empty($params['auth']) ? false : $params['auth'];
    }
}