<?php
/**
 * Copyright (c) Bytedance, Inc. and its affiliates. All Rights Reserved
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @package TikTok
 */

if ( ! defined( 'ABSPATH' ) ) {
	die;
}

require_once __DIR__ . '/../utils/utilities.php';

class Tt4b_Mapi_Class {

	/**
	 * The TikTok Ads endpoint base url.
	 *
	 * @var string
	 */
	protected $mapi_url;

	/**
	 * The TBP endpoint base url.
	 *
	 * @var string
	 */
	protected $tbp_url;

	/**
	 * The plugin endpoint base url.
	 *
	 * @var string
	 */
	protected $plugin_url;

	/**
	 * The wc_get_logger interface.
	 *
	 * @var WC_Logger_Interface
	 */
	protected $logger;

	/**
	 * Constructor
	 *
	 * @return void
	 */
	public function __construct( Logger $logger ) {
		$this->mapi_url   = 'https://business-api.tiktok.com/open_api/';
		$this->tbp_url    = 'https://business-api.tiktok.com/tbp/';
		$this->plugin_url = 'https://business-api.tiktok.com/plugin/';
		$this->logger     = $logger;
	}

	/**
	 * Initializes actions related to Tt4b_Mapi_Class such as eligibility information collection
	 */
	public function init() {
		add_action( 'tt4b_trust_signal_collection', array( $this, 'retrieve_eligibility_information' ), 1, 0 );
		add_action( 'tt4b_trust_signal_helper', array( $this, 'retrieve_eligibility_helper' ), 2, 1 );
	}

	/**
	 * Posts to business-api.tiktok.com
	 *
	 * @param string $endpoint     The endpoint for the mapi post
	 * @param string $access_token The MAPI issued access token
	 * @param array  $params       Whichever params to be included with the post
	 * @param string $version      The MAPI version used
	 * @param bool   $blocking     Whether the request should be synchronous
	 *
	 * @return string
	 */
	public function mapi_post( $endpoint, $access_token, $params, $version, $blocking = true ) {
		$url  = $this->mapi_url . $version . '/' . $endpoint;
		$args = array(
			'blocking'    => $blocking,
			'method'      => 'POST',
			'data_format' => 'body',
			'headers'     => array(
				'Access-Token' => $access_token,
				'Content-Type' => 'application/json',
			),
			'body'        => json_encode( $params ),
		);
		$response = wp_remote_post( $url, $args );
		if ($blocking) {
			$this->logger->log_request( $url, $args );
			$this->logger->log_response( __METHOD__, $response );
		}
		$body = wp_remote_retrieve_body( $response );
		return $body;
	}

	/**
	 * Get from business-api.tiktok.com
	 *
	 * @param string $endpoint     The endpoint for the mapi post
	 * @param string $access_token The MAPI issued access token
	 * @param array  $params       Whichever params to be included with the post
	 * @param string $version      The MAPI version used
	 *
	 * @return string
	 */
	public function mapi_get( $endpoint, $access_token, $params, $version ) {
		$url  = $this->mapi_url . $version . '/' . $endpoint . '?' . http_build_query( $params );
		$args = array(
			'method'  => 'GET',
			'headers' => array(
				'Access-Token' => $access_token,
				'Content-Type' => 'application/json',
			),
		);
		$this->logger->log_request( $url, $args );
		$result = wp_remote_get( $url, $args );
		$this->logger->log_response( __METHOD__, $result );
		$body = wp_remote_retrieve_body( $result );
		return $body;
	}

	/**
	 * Get from tbp/business_profile
	 *
	 * @param string $access_token         The MAPI issued access token
	 * @param string $external_business_id The exteneral business_id of the merchant
	 *
	 * @return string
	 */
	public function get_business_profile( $access_token, $external_business_id ) {
		// returns a raw API response from TikTok tbp/business_profile/get/ endpoint

		if ( false === $external_business_id ) {
			$this->logger->log( __METHOD__, 'external_business_id not found, exiting' );
			return '';
		}

		$url    = 'tbp/business_profile/get/';
		$params = array(
			'business_platform'    => 'WOO_COMMERCE',
			'external_business_id' => $external_business_id,
			'full_data'            => 1,
		);
		$result = $this->mapi_get( $url, $access_token, $params, 'v1.2' );
		return $result;
	}

	/**
	 *
	 * Get from api/user/location
	 *
	 * @return string
	 */
	public function get_user_location() {
		// returns a raw API response from TikTok api/user/location endpoint
		$url = 'https://ads.tiktok.com/creative_hub_server/api/user/location';
		$this->logger->log_request( $url, array() );
		$result = wp_remote_get( $url );
		$this->logger->log_response( __METHOD__, $result );
		return wp_remote_retrieve_body( $result );
	}


	/**
	 * Post to tbp
	 *
	 * @param string $external_data The external data
	 * @param string $endpoint      The endpoint
	 * @param string $version       The version
	 * @param array  $params        The body of the request
	 * @param int    $tbp_api_type  Which url to use based on the TBPApi abstract class
	 * @param bool   $blocking      Whether the request should be synchronous
	 *
	 * @return string
	 */
	public function tbp_post( $external_data, $endpoint, $version, $params, $tbp_api_type, $blocking = true ) {
		// posts to TBP
		switch ( $tbp_api_type ) {
			case TBPApi::PLUGIN:
				$domain = $this->plugin_url;
				break;
			default:
				$domain = $this->tbp_url;
				break;
		}
		$base_url = $domain . $version . '/' . $endpoint;
		$url      = $base_url . '?external_data=' . $external_data;
		$args     = array(
			'blocking'    => $blocking,
			'method'      => 'POST',
			'data_format' => 'body',
			'headers'     => array(
				'Content-Type' => 'application/json',
			),
			'body'        => json_encode( $params ),
		);
		$response = wp_remote_post( $url, $args );
		if ($blocking) {
			$this->logger->log_request( $url, $args );
			$this->logger->log_response( __METHOD__, $response );
		}
		return wp_remote_retrieve_body( $response );
	}

	/**
	 * Update from tbp/business_profile
	 *
	 * @param string  $access_token                           The MAPI issued access token
	 * @param string  $external_business_id                   The external business_id of the merchant
	 * @param integer $total_gmv                              The merchant's total gmv
	 * @param integer $total_orders                           The merchant's total orders
	 * @param integer $total_orders                           The merchant's tenure in days
	 * @param string  $current_tiktok_for_woocommerce_version The current tiktok-for-woocommerce version
	 *
	 * @return void
	 */
	public function update_business_profile( $access_token, $external_business_id, $total_gmv, $total_orders, $days_since_first_order, $current_tiktok_for_woocommerce_version ) {
		// updates the business_profile. Used for updating a merchants eligibility criteria.
		if ( false === $external_business_id ) {
			$this->logger->log( __METHOD__, 'external_business_id not found, exiting' );
		}

		$url             = 'tbp/business_profile/store/update/';
		$net_gmv         = array(
			array(
				'interval' => 'LIFETIME',
				'max'      => $total_gmv,
				'min'      => $total_gmv,
				'unit'     => 'CURRENCY',
			),
		);
		$net_order_count = array(
			array(
				'interval' => 'LIFETIME',
				'max'      => $total_orders,
				'min'      => $total_orders,
				'unit'     => 'COUNT',
			),
		);
		$tenure          = array(
			'min'  => $days_since_first_order,
			'max'  => $days_since_first_order,
			'unit' => 'DAYS',
		);
		$params          = array(
			'business_platform'    => 'WOO_COMMERCE',
			'external_business_id' => $external_business_id,
			'net_gmv'              => $net_gmv,
			'net_order_count'      => $net_order_count,
			'tenure'               => $tenure,
			'extra_data'           => $current_tiktok_for_woocommerce_version,
		);
		$this->mapi_post( $url, $access_token, $params, 'v1.2' );
	}

	/**
	 * Returns a raw API response from TikTok
	 * marketing_api/api/developer/app/create_auto_approve/
	 *
	 * @param string $smb_id       The merchants external_business_id
	 * @param string $smb_name     The MAPI issued access token
	 * @param string $redirect_uri The redirect_url (the store url)
	 *
	 * @return string|bool
	 */
	public function create_open_source_app( $smb_id, $smb_name, $redirect_uri ) {
		$url               = 'https://ads.tiktok.com/marketing_api/api/developer/app/create_auto_approve/';
		$open_source_token = '244e1de7-8dad-4656-a859-8dc09eea299d';
		$tries             = 0;
		$params            = array(
			'business_platform' => 'PROD',
			'smb_id'            => $smb_id,
			'smb_name'          => $smb_name,
			'redirect_url'      => $redirect_uri,
		);
		$args              = array(
			'method'      => 'POST',
			'data_format' => 'body',
			'headers'     => array(
				'Access-Token' => $open_source_token,
				'Content-Type' => 'application/json',
				'Referer'      => 'https://ads.tiktok.com',
			),
			'body'        => json_encode( $params ),
		);
		$this->logger->log_request( $url, $args );
		while ( $tries <= 3 ) {
			$response = wp_remote_post( $url, $args );
			++$tries;
			if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
				$this->logger->log_response( __METHOD__, $response );
			} else {
				$this->logger->log_response( __METHOD__, $response );
				return wp_remote_retrieve_body( $response );
			}
		}
		return false;
	}

	/**
	 * Returns a raw API response from TikTok oauth2/access_token_v2/ endpoint
	 *
	 * @param string $app_id    The MAPI app_id
	 * @param string $secret    The MAPI secret
	 * @param string $auth_code The auth_code
	 * @param string $version   The MAPI version used
	 *
	 * @return string
	 */
	public function get_access_token( $app_id, $secret, $auth_code, $version ) {
		$endpoint = 'oauth2/access_token/';
		$url      = $this->mapi_url . $version . '/' . $endpoint;
		$params   = array(
			'app_id'    => $app_id,
			'secret'    => $secret,
			'auth_code' => $auth_code,
		);
		$args     = array(
			'method'      => 'POST',
			'data_format' => 'body',
			'headers'     => array( 'Content-Type' => 'application/json' ),
			'body'        => json_encode( $params ),
		);
		$this->logger->log_request( $url, $args );
		$response = wp_remote_post( $url, $args );
		$this->logger->log_response( __METHOD__, $response );
		$body = wp_remote_retrieve_body( $response );
		return $body;
	}

	/**
	 * Returns a cron string with randomized hour and minute values for scheduling recurring eligibility collection
	 *
	 * @return string
	 */
	private function generate_cron_string() {
		$minute = rand( 0, 59 );
		$hour   = rand( 0, 23 );
		return '' . $minute . ' ' . $hour . ' * * 0-6';
	}

	/**
	 * Begins first eligibility information collection process, and scheduled recurring collection if not already scheduled
	 *
	 * @return void
	 */
	public function fetch_eligibility() {
		// only fetch if using woocommerce
		if ( ! did_action( 'woocommerce_loaded' ) > 0 ) {
			return;
		}

		$currentGroup = 'tt4b_version_' . get_option( 'tt4b_version' );
		if ( false === as_has_scheduled_action( 'tt4b_trust_signal_collection', array(), $currentGroup ) ) {
			// if no scheduled trust signal collection actions with $currentGroup group name, but there are scheduled actions (with no group name, or with other group names)
			// that means there are scheduled actions from a previous version that should be removed and replaced with scheduled actions from the latest
			if ( true === as_has_scheduled_action( 'tt4b_trust_signal_collection' ) ) {
				// deletes scheduled actions from previous version
				as_unschedule_all_actions( 'tt4b_trust_signal_collection' );
				as_unschedule_all_actions( 'tt4b_trust_signal_helper' );
			}
			as_enqueue_async_action( 'tt4b_trust_signal_collection' );
			$cronStr = $this->generate_cron_string();
			as_schedule_cron_action( strtotime( 'tomorrow' ), $cronStr, 'tt4b_trust_signal_collection', array(), $currentGroup );
		}
	}

	/**
	 * Retrieves eligibility information from merchants woocommerce store via creation of retrieve_eligibility_helper functions for batches of 20 orders
	 *
	 * @return void
	 */
	public function retrieve_eligibility_information() {
		// only fetch if using woocommerce
		if ( ! did_action( 'woocommerce_loaded' ) > 0 ) {
			return;
		}

		$args   = array(
			'post_status' => 'wc-completed',
			'paginate'    => true,
			'limit'       => 100,
		);
		$result = wc_get_orders( $args );
		$pages  = $result->max_num_pages;
		update_option( 'tt4b_mapi_total_gmv', 0 );
		update_option( 'tt4b_mapi_total_orders', 0 );
		update_option( 'tt4b_mapi_tenure', 0 );
		update_option( 'tt4b_eligibility_page_total', $pages );
		$oldest_orders = ( new WC_Order_Query(
			array(
				'limit'   => 1,
				'orderby' => 'date',
				'order'   => 'ASC',
			)
		) )->get_orders();
		if ( count( $oldest_orders ) > 0 ) {
			$oldest_order_timestamp = $oldest_orders[0]->get_date_created()->getTimestamp();
			$mapi_tenure            = (int) ( ( time() - $oldest_order_timestamp ) / DAY_IN_SECONDS );
			update_option( 'tt4b_mapi_tenure', $mapi_tenure );
		}
		if ( false === as_has_scheduled_action( 'tt4b_trust_signal_helper', array( 'page' => 1 ) ) ) {
			as_enqueue_async_action( 'tt4b_trust_signal_helper', array( 'page' => 1 ) );
		}
	}

	/**
	 * Helper function used to calculate eligibility information in batches of 20
	 *
	 * @param integer $page The page of orders from woocommerce
	 *
	 * @return void
	 */
	public function retrieve_eligibility_helper( $page ) {
		// only fetch if using woocommerce
		if ( ! did_action( 'woocommerce_loaded' ) > 0 ) {
			return;
		}

		$orders = wc_get_orders(
			array(
				'post_status' => 'wc-completed',
				'limit'       => 100,
				'page'        => $page,
			)
		);
		foreach ( $orders as $order ) {
			if ( is_null( $order ) ) {
				break;
			}
			$order_total = $order->get_total();
			if ( $order_total > 0 ) {
				$mapi_total_gmv  = get_option( 'tt4b_mapi_total_gmv' );
				$mapi_total_gmv += intval( $order_total );
				update_option( 'tt4b_mapi_total_gmv', $mapi_total_gmv );
				$mapi_total_orders = get_option( 'tt4b_mapi_total_orders' );
				++$mapi_total_orders;
				update_option( 'tt4b_mapi_total_orders', $mapi_total_orders );
			}
			if ( 0 === count( $orders ) ) {
				break;
			}
		}
		$page_total = get_option( 'tt4b_eligibility_page_total' );
		++$page;
		if ( ( $page <= $page_total ) && ( false === as_has_scheduled_action( 'tt4b_trust_signal_helper', array( 'page' => $page ) ) ) ) {
			as_enqueue_async_action( 'tt4b_trust_signal_helper', array( 'page' => $page ) );
		} else {
			$access_token         = get_option( 'tt4b_access_token' );
			$external_business_id = get_option( 'tt4b_external_business_id' );
			$total_gmv            = intval( get_option( 'tt4b_mapi_total_gmv' ) );
			$total_orders         = intval( get_option( 'tt4b_mapi_total_orders' ) );
			$tenure               = intval( get_option( 'tt4b_mapi_tenure' ) );
			$version              = get_option( 'tt4b_version' );
			$this->update_business_profile( $access_token, $external_business_id, $total_gmv, $total_orders, $tenure, $version );
		}
	}

	/**
	 * TTS Disconnect
	 *
	 * @param string $external_data The external data
	 *
	 * @return void
	 */
	public function tts_shop_disconnect( $external_data ) {
		$base_url = 'https://business-api.tiktok.com/tbp/v2.0/shop/connection/disconnect';
		$url      = $base_url . '?external_data=' . $external_data;
		$args     = array(
			'method'  => 'POST',
			'headers' => array(
				'Content-Type' => 'application/json',
			),
		);
		$this->logger->log_request( $url, $args );
		$response = wp_remote_post( $url, $args );
		$this->logger->log_response( __METHOD__, $response );
	}
}
