<?php
/**
 * @package   Essential_Grid
 * @author    ThemePunch <info@themepunch.com>
 * @link      https://www.essential-grid.com/
 * @copyright 2025 ThemePunch
 */

if ( ! defined( 'ABSPATH' ) ) exit();

class Essential_Grid_LoadBalancer
{

	/**
	 * @var string[]
	 */
	public $servers = [];
	
	/**
	 * @var string[]
	 */
	public $defaults = [ 'themepunch.tools', 'themepunch-ext-a.tools', 'themepunch-ext-b.tools', 'themepunch-ext-c.tools' ];
	

	/**
	 * set the server list on construct
	 */
	public function __construct()
	{
				
		$this->servers = get_option( 'essgrid_servers', [] );
		if ( empty( $this->servers ) ) {
			$this->servers = $this->defaults;
			shuffle( $this->servers );
			update_option( 'essgrid_servers', $this->servers );
		}
	}

	/**
	 * @return string[]
	 */
	public function get_defaults()
	{
		return $this->defaults;
	}

	/**
	 * get the url depending on the purpose, here with key, you can switch do a different server
	 *
	 * @param string $purpose
	 * @param int $key
	 * @param bool $force_http
	 *
	 * @return string
	 * @throws EssentialGridException
	 */
	public function get_url( $purpose, $key = 0, $force_http = false )
	{
		$url_arr = [];
					$url_arr[] = $force_http ? 'http://' : 'https://';

		switch ( $purpose ) {
			case 'updates':
			case 'templates':
			case 'library':
				$url_arr[] .= $purpose;
				break;
			default:
				throw new EssentialGridException( esc_html__( 'Wrong Url subdomain.', 'essential-grid' ) );
		}

				$url_arr[] = '.';
		$url_arr[] = ( ! isset( $this->servers[ $key ] ) ) ? reset( $this->servers ) : $this->servers[ $key ];
		return implode( '', $url_arr );
	}

	/**
	 * refresh the server list to be used, will be done once in a month
	 *
	 * @param bool $force
	 * @return void
	 * @throws EssentialGridException
	 */
	public function refresh_server_list( $force = false )
	{
		$last_check = get_option( 'essgrid_server_refresh' );
		if ( empty( $last_check ) ) {
			$last_check = time();
			update_option( 'essgrid_server_refresh', $last_check );
		}
		
		if ( $force || time() - $last_check > 60 * 60 * 24 * 30 ) {
			$data    = [
				'item'    => urlencode( ESG_PLUGIN_SLUG ),
				'version' => urlencode( ESG_REVISION ),
			];
			$url     = apply_filters( 'essgrid_loadbalancer_get_server_list_url', 'https://updates.themepunch.tools/get_server_list.php' );
			$request = $this->call_url( $url, $data );
			if ( ! is_wp_error( $request ) ) {
				if ( $response = maybe_unserialize( $request['body'] ) ) {
					$this->servers = esg_json_decode( $response, [] );
					update_option( 'essgrid_servers', $this->servers );
					update_option( 'essgrid_server_refresh', time() );
				}
			}
		}
	}
	
	/**
	 * move the server list, to take the next server as the one currently seems unavailable
	 */
	public function move_server_list()
	{
		$this->servers[] = array_shift( $this->servers );
		update_option( 'essgrid_servers', $this->servers );
	}

	/**
	 * call themepunch URL and retrieve data
	 *
	 * @param string $url can be full URL or just a file name. In this case url is built using $subdomain param
	 * @param array $data
	 * @param string $subdomain
	 * @param bool $force_http
	 * @return array|WP_Error The response or WP_Error on failure.
	 */
	public function call_url( $url, $data = [], $subdomain = 'updates', $force_http = false )
	{
		global $wp_version;
		
		// add version if not passed
		$data['version'] = ( ! isset( $data['version'] ) ) ? urlencode( ESG_REVISION ) : $data['version'];
		
		$done  = false;
		$count = 0;
		
		do {
			if ( ! preg_match( "/^https?:\/\//i", $url ) ) {
				// url is filename
				try {
					$server = $this->get_url( $subdomain, 0, $force_http );
					$url    = $server . '/' . ltrim( $url, '/' );
				} catch ( Exception $e ) {
					return new WP_Error( 'loadbalancer_get_url', $e->getMessage() );
				}
				
			} else {
				// full URL
				if ( $force_http ) {
					$url = preg_replace( "/^https:\/\//i", "http://", $url );
				}
			}
			
			$request = wp_safe_remote_post( $url, [
				'user-agent' => 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' ),
				'body'      => $data,
				'timeout'   => 45,
			] );
			
			$response_code = wp_remote_retrieve_response_code( $request );
			if ( $response_code == 200 ) {
				$done = true;
			} else {
				$this->move_server_list();
			}
			
			$count++;
		} while ( $done == false && $count < 3 );
		
		return $request;
	}
}

