<?php

/**
 * AJAX endpoints for the built-blocks library list table.
 *
 * @package LolaCore\BlockBuilder\Admin
 * @license GPL-2.0-or-later
 */

declare(strict_types=1);

namespace LolaCore\BlockBuilder\Admin;

use LolaCore\BlockBuilder\Gate\BlockBuilderGate;
use LolaCore\BlockBuilder\Packaging\BlockLocalInstaller;
use LolaCore\BlockBuilder\Playground\BlockBlueprintGenerator;
use LolaCore\BlockBuilder\PostType\BlockPostType;
use LolaCore\I18n\ErrorMessages;

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

/**
 * Playground URL and local install actions from the Block Builder library screen.
 */
class BlockBuilderAjax {

	private BlockBlueprintGenerator $blueprintGenerator;
	private BlockLocalInstaller $localInstaller;

	public function __construct(
		?BlockBlueprintGenerator $blueprintGenerator = null,
		?BlockLocalInstaller $localInstaller = null
	) {
		$this->blueprintGenerator = $blueprintGenerator ?? new BlockBlueprintGenerator();
		$this->localInstaller      = $localInstaller ?? new BlockLocalInstaller();
	}

	public function register(): void {
		add_action('wp_ajax_lolacore_bb_playground', array($this, 'handlePlayground'));
		add_action('wp_ajax_lolacore_bb_playground_opened', array($this, 'handlePlaygroundOpened'));
		add_action('wp_ajax_lolacore_bb_playground_attest', array($this, 'handlePlaygroundAttest'));
		add_action('wp_ajax_lolacore_bb_install', array($this, 'handleInstall'));
		add_action('wp_ajax_lolacore_bb_uninstall', array($this, 'handleUninstall'));
	}

	public function handlePlayground(): void {
		$this->verifySecurity();

		$post_id = $this->readPostId();
		if ($post_id <= 0) {
			wp_send_json_error(array('message' => __('Invalid block ID.', 'lolacore')), 400);
		}

		$this->assertBuiltBlockPost($post_id);

		try {
			$url  = $this->blueprintGenerator->build($post_id);
			$post = get_post($post_id);
			$name = $post instanceof \WP_Post ? $post->post_title : '';

			wp_send_json_success(
				array_merge(
					array(
						'playground_url' => $url,
						'post_id'        => $post_id,
						'name'           => $name,
						'block_name'     => (string) get_post_meta($post_id, BlockPostType::META_NAME, true),
					),
					BlockBuilderGate::uiFlags($post_id)
				)
			);
		} catch (\Throwable $e) {
			wp_send_json_error(array('message' => $e->getMessage()), 500);
		}
	}

	public function handlePlaygroundOpened(): void {
		$this->verifySecurity();

		$post_id = $this->readPostId();
		if ($post_id <= 0) {
			wp_send_json_error(array('message' => __('Invalid block ID.', 'lolacore')), 400);
		}

		$this->assertBuiltBlockPost($post_id);
		BlockBuilderGate::markOpened($post_id);

		wp_send_json_success(BlockBuilderGate::uiFlags($post_id));
	}

	public function handlePlaygroundAttest(): void {
		$this->verifySecurity();

		$post_id = $this->readPostId();
		if ($post_id <= 0) {
			wp_send_json_error(array('message' => __('Invalid block ID.', 'lolacore')), 400);
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Verified in verifySecurity().
		$outcome = isset($_POST['outcome']) ? sanitize_key((string) wp_unslash($_POST['outcome'])) : '';

		$this->assertBuiltBlockPost($post_id);

		if ($outcome === 'attested') {
			BlockBuilderGate::markAttested($post_id);
		} elseif ($outcome === 'failed') {
			BlockBuilderGate::markFailed($post_id);
		} else {
			wp_send_json_error(array('message' => __('Invalid attestation outcome.', 'lolacore')), 400);
		}

		wp_send_json_success(BlockBuilderGate::uiFlags($post_id));
	}

	public function handleInstall(): void {
		$this->verifySecurity();

		$post_id = $this->readPostId();
		if ($post_id <= 0) {
			wp_send_json_error(array('message' => __('Invalid block ID.', 'lolacore')), 400);
		}

		$this->assertBuiltBlockPost($post_id);

		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Verified in verifySecurity().
		$overwrite = ! empty($_POST['overwrite']);

		if (! $overwrite && $this->localInstaller->isAlreadyInstalled($post_id)) {
			wp_send_json_error(
				array(
					'message'         => __('A version is already installed. Overwrite it?', 'lolacore'),
					'needs_overwrite' => true,
				),
				409
			);
		}

		try {
			BlockBuilderGate::assertInstallAllowed($post_id);
			BlockBuilderGate::assertLoadSafe($post_id);
			$result = $this->localInstaller->install($post_id, $overwrite);

			wp_send_json_success(
				array(
					'message'       => (string) ( $result['message'] ?? '' ),
					'post_id'       => $post_id,
					'install_slug'  => (string) ( $result['install_slug'] ?? '' ),
					'install_path'  => (string) ( $result['install_path'] ?? '' ),
					'registered'    => (bool) ( $result['registered'] ?? false ),
					'overwritten'   => (bool) ( $result['overwritten'] ?? false ),
				)
			);
		} catch (\Throwable $e) {
			$payload = array('message' => $e->getMessage());

			// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Verified in verifySecurity().
			if (! $overwrite && $this->localInstaller->isAlreadyInstalled($post_id)) {
				$payload['needs_overwrite'] = true;
			}

			wp_send_json_error($payload, 500);
		}
	}

	public function handleUninstall(): void {
		$this->verifySecurity();

		$post_id = $this->readPostId();
		if ($post_id <= 0) {
			wp_send_json_error(array('message' => __('Invalid block ID.', 'lolacore')), 400);
		}

		$this->assertBuiltBlockPost($post_id);

		try {
			if (! $this->localInstaller->isAlreadyInstalled($post_id)) {
				wp_send_json_error(
					array('message' => __('This block is not installed on the site yet.', 'lolacore')),
					400
				);
			}

			$result = $this->localInstaller->uninstall($post_id);

			wp_send_json_success(
				array(
					'message'      => (string) ( $result['message'] ?? '' ),
					'post_id'      => $post_id,
					'install_slug' => (string) ( $result['install_slug'] ?? '' ),
					'install_path' => (string) ( $result['install_path'] ?? '' ),
					'registered'   => (bool) ( $result['registered'] ?? false ),
				)
			);
		} catch (\Throwable $e) {
			wp_send_json_error(array('message' => $e->getMessage()), 500);
		}
	}

	private function verifySecurity(): void {
		if (! check_ajax_referer('lolacore_admin_nonce', '_ajax_nonce', false)) {
			wp_send_json_error(array('message' => ErrorMessages::sessionExpired()), 403);
		}

		if (! current_user_can('manage_options')) {
			wp_send_json_error(array('message' => ErrorMessages::unauthorized()), 403);
		}
	}

	private function readPostId(): int {
		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Verified in verifySecurity().
		return isset($_POST['post_id']) ? absint($_POST['post_id']) : 0;
	}

	private function assertBuiltBlockPost( int $post_id ): void {
		$post = get_post($post_id);
		if (! $post instanceof \WP_Post || $post->post_type !== BlockPostType::POST_TYPE) {
			wp_send_json_error(array('message' => __('Built block not found.', 'lolacore')), 404);
		}
	}
}
