<?php

use Psr\Http\Message\{RequestInterface, ResponseInterface};
use GuzzleHttp\{Client, HandlerStack, Middleware, RetryMiddleware};

final class Zbr_Plugin_Core
{
	const SCHEDULE = 'hourly';
	protected static $_instance = null;
	protected PSR\Log\LoggerInterface $logger;

	public function __construct()
	{
		$this->includes();
		$this->init_hooks();

		$this->logger = new Monolog\Logger('zbr');
		$this->logger->pushHandler(new Monolog\Handler\ErrorLogHandler());
		Monolog\Registry::addLogger($this->logger);
	}

	private function includes()
	{
		include_once ZBR_DIR . '/includes/class-zbr-news-article.php';
		include_once ZBR_DIR . '/includes/class-zbr-loader.php';
		include_once ZBR_DIR . '/includes/class-zbr-post-types.php';

		include_once ZBR_DIR . '/includes/zbr-news-functions.php';

		if (is_admin()) {
			include_once ZBR_DIR . '/includes/admin/class-zbr-admin.php';
		}

		if (!is_admin()) {
			$this->frontend_includes();
		}

		// include elementor side
		include_once ZBR_DIR . '/includes/elementor/plugin.php';
	}

	private function init_hooks()
	{
		add_action('after_setup_theme', [$this, 'include_template_functions'], 11);
		add_action('init', [$this, 'init'], 0);
		add_filter('single_template', [$this, 'load_template']);

		add_action('zbr_create_import', [$this, 'create_new_import']);
		add_action('zbr_run_import', [$this, 'run_import']);

		add_action('get_the_author_display_name', [$this, 'get_the_author_display_name']);
		add_action('the_author', [$this, 'get_the_author_display_name']);
		add_action('get_avatar_data', [$this, 'get_avatar_data']);

		//add_action('parse_query', [$this, 'filter_attachments_from_attachments_query']);
		add_filter('ajax_query_attachments_args', [$this, 'filter_attachments_from_ajax_attachments_query']);

		add_action('before_delete_post', [$this, 'delete_post_attachments']);
	}

	public function filter_attachments_from_attachments_query($query)
	{
		global $pagenow, $typenow;

		if ('upload.php' !== $pagenow || 'attachment' !== $typenow) {
			return;
		}

		$this->logger->info('altering query to exlucde attachments without _zbr_is_attachment fields');
		$query->query_vars['meta_query'] = [
			[
				'key' => '_zbr_is_attachment',
				'compare' => 'NOT EXISTS',
			],
		];
	}

	/* 
	The ajax_query_attachments_args filter is used to filter the query that fetches the attachments displayed in the media library modal on the post edit screen.
	*/
	public function filter_attachments_from_ajax_attachments_query($args)
	{
		global $pagenow, $typenow;

		$meta_query = array(
			array(
				'key'     => '_zbr_is_attachment',
				'compare' => 'NOT EXISTS',
			),
		);

		// If there was already a meta query, let's AND it via
		// nesting it with our new one. No need to specify the
		// relation, as it defaults to AND.
		if (!empty($args['meta_query'])) {
			$meta_query[] = $args['meta_query'];
		}
		$args['meta_query'] = $meta_query;

		return $args;
	}

	public function get_the_author_display_name($value)
	{
		if (get_post_type() == 'zbr_news') {
			/** @var \OpenAPI\Client\Model\AuthorDto[] $authors */
			$authors = get_post_meta(get_the_ID(), '_zbr_authors', true);
			if (isset($authors) && count($authors) > 0) {
				return $authors[0]->getName();
			}
		}
		return $value;
	}

	public function get_avatar_data($args)
	{
		if (get_post_type() == 'zbr_news') {
			/** @var \OpenAPI\Client\Model\AuthorDto[] $authors */
			$authors = get_post_meta(get_the_ID(), '_zbr_authors', true);
			if (isset($authors) && count($authors) > 0) {
				$avatar = $authors[0]->getAvatar();

				if (isset($avatar)) {
					return wp_parse_args([
						'found_avatar' => true,
						'url' => $avatar . '?width=' . $args['width'] . '&height=' . $args["height"] . "&mode=crop",
					], $args);
				}
			}
		}
		return $args;
	}

	public function init()
	{

		do_action('before_zbr_init');

		do_action('zbr_init');

		$args = array();
		if (!wp_next_scheduled('zbr_run_import', $args)) {
			$this->logger->info('scheduling zbr_run_import');
			wp_schedule_event(time(), self::SCHEDULE, 'zbr_run_import', $args, true);
		}
	}

	public function run_import($id = null)
	{
		if ($id == null) {
			$id = Zbr_Plugin_Core::instance()->create_new_import();
		}

		Zbr_Plugin_Core::instance()->load_api_data($id);
	}

	public function include_template_functions()
	{
		require_once ZBR_DIR . '/includes/zbr-template-functions.php';
	}

	public function manage_zbr_news_posts_custom_column($column_name, $post_id)
	{
		switch ($column_name) {
			case "media":
				$media = get_post_meta($post_id, 'media', true);
				echo "<span>" . count($media) . "</span>";
				break;
		}
	}

	public function frontend_includes()
	{
		include_once ZBR_DIR . '/includes/zbr-template-hooks.php';
		# include_once ZBR_DIR . '/includes/class-zbr-frontend-includes.php';
	}

	public function load_template($template)
	{
		global $post;

		# TODO: implement this based on the supported post_types
		$post_type = $post->post_type;

		if ($post_type != 'zbr_news') {
			return $template;
		}

		if (locate_template(array("single-$post_type.php")) !== $template) {
			return ZBR_DIR . "/templates/single-$post_type.php";
		}

		return $template;
	}

	public function on_activation()
	{
		global $wpdb;

		require_once ABSPATH . 'wp-admin/includes/upgrade.php';

		// Register the plugin loading log table
		$tablePrefix = $wpdb->prefix . 'zbr_';
		$charset_collate = $wpdb->get_charset_collate();
		$logTable = $tablePrefix . 'loading_logs';
		$sql = "CREATE TABLE $logTable (
            id mediumint(9) NOT NULL AUTO_INCREMENT,
            createdAt datetime NOT NULL,
            finishedAt datetime NULL,
            status tinyint NOT NULL,
            exception mediumtext NULL,
            PRIMARY KEY  (id)
        ) $charset_collate;";

		dbDelta($sql);
	}

	public function on_deactivation()
	{
		global $wpdb;

		$tablePrefix = $wpdb->prefix . 'zbr_';

		$logTable = $tablePrefix . 'loading_logs';
		$sql = "drop table $logTable";

		$wpdb->query($sql);

		// remove all of the posts
		$query = get_posts([
			'post_type' => ['zbr_news', 'zbr_agenda', 'zbr_zone', 'zbr_zone_team_member'],
			'nopaging' => true,
			'post_status' => 'publish, trash, auto-draft, draft, future'
		]);

		foreach ($query as $post) {
			write_log('deleting post ' . $post->ID);
			wp_delete_post($post->ID, true);
		}

		// Remove all of the post_types asociated with our plugin
		unregister_post_type('zbr_news');
		unregister_post_type('zbr_agenda');
		unregister_post_type('zbr_zone');
		unregister_post_type('zbr_zone_team_member');
		// unregister_taxonomy('zbr_tx_zone');
		// unregister_taxonomy('zbr_tx_author');
	}

	public function delete_post_attachments($id)
	{
		// If the post type starts with zbr_, delete all of the attachments
		$postType = get_post_type($id);

		if (substr($postType, 0, 4) == 'zbr_') {
			$attachments = get_attached_media('', $id);
			foreach ($attachments as $attachment) {
				write_log('deleting attachment ' . $attachment->ID);
				wp_delete_attachment($attachment->ID, 'true');
			}
		}
	}

	public static function load_api_data($import_id = null)
	{
		$logger = Monolog\Registry::zbr();

		$api_key = get_option('zbr_api_key');
		$api_host = get_option('zbr_api_host');

		if (!isset($api_host) || $api_host == '') {
			$api_host = 'https://api.ziber.eu';
		}

		$logger->info('Loading API data from ' . $api_host . ' with API key ' . $api_key);

		$config = new OpenAPI\Client\Configuration();
		$config->setHost($api_host);

		$client = Zbr_Plugin_Core::create_client($api_key, $logger);

		$loader = new Zbr_Loader($client, $config, $logger);
		$loader->load_data($import_id);
	}

	private static function create_client($api_key)
	{
		$maxRetries = 3;

		$decider = function (int $retries, RequestInterface $request, ResponseInterface $response = null) use ($maxRetries): bool {
			return
				$retries < $maxRetries
				&& null !== $response
				&& 429 === $response->getStatusCode();
		};

		$delay = function (int $retries, ResponseInterface $response): int {
			$logger = Monolog\Registry::zbr();
			$logger->warning('Hitting the rate limit');

			if (!$response->hasHeader('Retry-After')) {
				return RetryMiddleware::exponentialDelay($retries);
			}

			$retryAfter = $response->getHeaderLine('Retry-After');

			if (!is_numeric($retryAfter)) {
				$retryAfter = (new DateTime($retryAfter))->getTimestamp() - time();
			}
			$logger->warning('Retrying after' . ($retryAfter * 1000) . ' seconds');

			return (int) $retryAfter * 1000;
		};

		$stack = HandlerStack::create();
		$stack->push(Middleware::retry($decider, $delay));
		return new GuzzleHttp\Client([
			"headers" => ["X-Api-Key" => $api_key],
			"handler" => $stack
		]);;
	}

	public static function create_new_import(): int
	{

		global $wpdb;

		$tableName = $wpdb->prefix . 'zbr_loading_logs';

		$now = date("Y-m-d H:i:s");

		$wpdb->insert($tableName, array(
			'createdAt' => $now,
			'status' => 0,
		));
		return $wpdb->insert_id;
	}

	public static function instance()
	{
		if (is_null(self::$_instance)) {
			self::$_instance = new self();
		}
		return self::$_instance;
	}
}
