<?php
/**
 * CPT Builder Module
 *
 * Lets administrators create and manage Custom Post Types from the dashboard.
 * Registers stored post types on `init` (front and back end) and exposes a
 * dedicated admin builder. All hooks run only when the module is enabled.
 *
 * Definitions are stored in a single non-autoloaded option
 * ({@see Storage::OPTION}) for cleanup on uninstall.
 *
 * @package CptBuilder
 * @since 1.0.0
 */

namespace CptBuilder;

use WP_PowerSuite\Core\AbstractModule;
use WP_PowerSuite\Core\Contracts\DataLocationsInterface;

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

require_once __DIR__ . '/includes/config.php';
require_once __DIR__ . '/includes/class-storage.php';
require_once __DIR__ . '/includes/class-validator.php';
require_once __DIR__ . '/includes/class-registrar.php';
require_once __DIR__ . '/includes/class-import-export.php';
require_once __DIR__ . '/includes/class-php-export.php';
require_once __DIR__ . '/includes/class-admin.php';
require_once __DIR__ . '/includes/class-taxonomy-storage.php';
require_once __DIR__ . '/includes/class-taxonomy-validator.php';
require_once __DIR__ . '/includes/class-taxonomy-registrar.php';
require_once __DIR__ . '/includes/class-taxonomy-import-export.php';
require_once __DIR__ . '/includes/class-taxonomy-admin.php';

/**
 * CPT Builder module bootstrap.
 */
class Module extends AbstractModule implements DataLocationsInterface {

	private const MODULE_ID      = 'cpt-builder';
	private const MODULE_VERSION = '1.2.0';

	private ?Storage $storage = null;

	private ?Registrar $registrar = null;

	private ?Admin $admin = null;

	private ?TaxonomyStorage $taxonomy_storage = null;

	private ?TaxonomyRegistrar $taxonomy_registrar = null;

	private ?TaxonomyAdmin $taxonomy_admin = null;

	public function getId(): string {
		return self::MODULE_ID;
	}

	public function getName(): string {
		return __( 'CPT Builder', 'wp-powersuite' );
	}

	public function getVersion(): string {
		return self::MODULE_VERSION;
	}

	public function getDescription(): string {
		return __( 'Create and manage Custom Post Types and Taxonomies from the admin—labels, visibility, REST, permalinks, capabilities, supports, connections, plus import/export.', 'wp-powersuite' );
	}

	public function getContext(): string|array {
		return [ 'admin', 'frontend', 'ajax', 'rest' ];
	}

	public function getDependencies(): array {
		return [];
	}

	/**
	 * Declare storage locations for uninstall cleanup.
	 *
	 * @return array{options: array<int, string>}
	 */
	public function getDataLocations(): array {
		return [
			'options' => [
				Storage::OPTION,
				Storage::FLUSH_FLAG,
				TaxonomyStorage::OPTION,
			],
		];
	}

	private function isLicenseActive(): bool {
		return function_exists( 'wp_powersuite_is_license_valid' ) && wp_powersuite_is_license_valid();
	}

	/**
	 * Register hooks (runs once per request, only when the module is enabled).
	 */
	protected function registerHooks(): void {
		if ( ! $this->isLicenseActive() ) {
			return;
		}

		$registrar          = $this->registrar();
		$taxonomy_registrar = $this->taxonomyRegistrar();

		add_action( 'init', [ $taxonomy_registrar, 'registerAll' ], 8 );
		add_action( 'init', [ $registrar, 'registerAll' ], 9 );
		// Flush on init (front + admin) so permalinks recover without waiting for an admin page load.
		add_action( 'init', [ $this, 'maybeFlushRewrites' ], 999 );

		if ( ! is_admin() ) {
			return;
		}

		$admin          = $this->admin();
		$taxonomy_admin = $this->taxonomyAdmin();

		add_action( 'admin_menu', [ $this, 'registerMenu' ], 20 );
		add_action( 'admin_enqueue_scripts', [ $this, 'enqueueAssets' ] );

		add_action( 'admin_post_wpps_cptb_save', [ $admin, 'handleSave' ] );
		add_action( 'admin_post_wpps_cptb_delete', [ $admin, 'handleDelete' ] );
		add_action( 'admin_post_wpps_cptb_duplicate', [ $admin, 'handleDuplicate' ] );
		add_action( 'admin_post_wpps_cptb_toggle', [ $admin, 'handleToggle' ] );
		add_action( 'admin_post_wpps_cptb_export', [ $admin, 'handleExport' ] );
		add_action( 'admin_post_wpps_cptb_export_php', [ $admin, 'handleExportPhp' ] );
		add_action( 'admin_post_wpps_cptb_import', [ $admin, 'handleImport' ] );

		add_action( 'admin_post_wpps_cptb_tax_save', [ $taxonomy_admin, 'handleSave' ] );
		add_action( 'admin_post_wpps_cptb_tax_delete', [ $taxonomy_admin, 'handleDelete' ] );
		add_action( 'admin_post_wpps_cptb_tax_duplicate', [ $taxonomy_admin, 'handleDuplicate' ] );
		add_action( 'admin_post_wpps_cptb_tax_toggle', [ $taxonomy_admin, 'handleToggle' ] );
		add_action( 'admin_post_wpps_cptb_tax_export', [ $taxonomy_admin, 'handleExport' ] );
		add_action( 'admin_post_wpps_cptb_tax_export_php', [ $taxonomy_admin, 'handleExportPhp' ] );
		add_action( 'admin_post_wpps_cptb_tax_import', [ $taxonomy_admin, 'handleImport' ] );
	}

	/**
	 * Lazily resolve the storage layer.
	 */
	private function storage(): Storage {
		if ( $this->storage === null ) {
			$this->storage = new Storage();
		}

		return $this->storage;
	}

	/**
	 * Lazily resolve the registrar.
	 */
	private function registrar(): Registrar {
		if ( $this->registrar === null ) {
			$this->registrar = new Registrar( $this->storage() );
		}

		return $this->registrar;
	}

	/**
	 * Lazily resolve the admin controller (and its dependencies).
	 */
	private function admin(): Admin {
		if ( $this->admin === null ) {
			$validator     = new Validator( $this->storage() );
			$import_export = new ImportExport( $this->storage(), $validator );
			$php_export    = new PhpExport(
				$this->storage(),
				$this->registrar(),
				$this->taxonomyStorage(),
				$this->taxonomyRegistrar()
			);
			$this->admin   = new Admin( $this->storage(), $validator, $import_export, $php_export );
		}

		return $this->admin;
	}

	/**
	 * Lazily resolve the taxonomy storage layer.
	 */
	private function taxonomyStorage(): TaxonomyStorage {
		if ( $this->taxonomy_storage === null ) {
			$this->taxonomy_storage = new TaxonomyStorage();
		}

		return $this->taxonomy_storage;
	}

	/**
	 * Lazily resolve the taxonomy registrar.
	 */
	private function taxonomyRegistrar(): TaxonomyRegistrar {
		if ( $this->taxonomy_registrar === null ) {
			$this->taxonomy_registrar = new TaxonomyRegistrar( $this->taxonomyStorage() );
		}

		return $this->taxonomy_registrar;
	}

	/**
	 * Lazily resolve the taxonomy admin controller (and its dependencies).
	 */
	private function taxonomyAdmin(): TaxonomyAdmin {
		if ( $this->taxonomy_admin === null ) {
			$validator            = new TaxonomyValidator( $this->taxonomyStorage() );
			$import_export        = new TaxonomyImportExport( $this->taxonomyStorage(), $validator );
			$this->taxonomy_admin = new TaxonomyAdmin( $this->taxonomyStorage(), $validator, $import_export );
		}

		return $this->taxonomy_admin;
	}

	/**
	 * Render the builder page, dispatching to the active section controller.
	 */
	public function renderBuilderPage(): void {
		if ( ! $this->isLicenseActive() ) {
			$license_page_url = \WP_PowerSuite\Core\NetworkScope::adminUrl( 'admin.php', [ 'page' => 'powersuite-license' ] );
			?>
			<div class="wrap wpps-cptb">
				<div class="notice notice-warning">
					<p>
						<?php esc_html_e( 'CPT Builder requires an active license. Activate your license to create and manage custom post types and taxonomies.', 'wp-powersuite' ); ?>
						<a href="<?php echo esc_url( $license_page_url ); ?>"><?php esc_html_e( 'Activate license', 'wp-powersuite' ); ?></a>
					</p>
				</div>
			</div>
			<?php
			return;
		}

		$section = isset( $_GET['section'] ) ? sanitize_key( wp_unslash( (string) $_GET['section'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only routing.

		if ( $section === TaxonomyAdmin::SECTION ) {
			$this->taxonomyAdmin()->renderPage();
			return;
		}

		$this->admin()->renderPage();
	}

	/**
	 * Register the builder submenu under WP PowerSuite.
	 */
	public function registerMenu(): void {
		$this->registerPowerSuiteSubmenu(
			__( 'CPT Builder', 'wp-powersuite' ),
			__( 'CPT Builder', 'wp-powersuite' ),
			Admin::PAGE_SLUG,
			[ $this, 'renderBuilderPage' ]
		);
	}

	/**
	 * Flush rewrite rules once after a change (deferred to admin load).
	 */
	public function maybeFlushRewrites(): void {
		$storage = $this->storage();
		if ( ! $storage->needsFlush() ) {
			return;
		}

		flush_rewrite_rules( false );
		$storage->clearFlushFlag();
	}

	/**
	 * Enqueue builder assets only on the CPT Builder screen.
	 *
	 * @param string $hook Current admin page hook suffix.
	 */
	public function enqueueAssets( string $hook ): void {
		$current = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( (string) $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only screen check.
		if ( $current !== Admin::PAGE_SLUG ) {
			return;
		}

		$base    = defined( 'WP_POWERSUITE_PLUGIN_URL' ) ? WP_POWERSUITE_PLUGIN_URL : '';
		$version = defined( 'WP_POWERSUITE_VERSION' ) ? WP_POWERSUITE_VERSION : self::MODULE_VERSION;

		wp_enqueue_style( 'dashicons' );

		// Reuse the shared PowerSuite admin styling for the hero/cards.
		if ( $base !== '' && ! wp_style_is( 'wp-powersuite-admin', 'registered' ) ) {
			wp_register_style( 'wp-powersuite-admin', $base . 'src/Admin/Assets/app.css', [ 'wp-admin' ], $version );
		}
		wp_enqueue_style( 'wp-powersuite-admin' );

		// Version on file mtime so browsers never serve stale module assets.
		$css_file = __DIR__ . '/assets/cptb-admin.css';
		$js_file  = __DIR__ . '/assets/cptb-admin.js';

		wp_enqueue_style(
			'wpps-cptb-admin',
			$base . 'modules/cpt-builder/assets/cptb-admin.css',
			[ 'wp-powersuite-admin', 'dashicons' ],
			is_readable( $css_file ) ? (string) filemtime( $css_file ) : self::MODULE_VERSION
		);

		wp_enqueue_script(
			'wpps-cptb-admin',
			$base . 'modules/cpt-builder/assets/cptb-admin.js',
			[ 'jquery' ],
			is_readable( $js_file ) ? (string) filemtime( $js_file ) : self::MODULE_VERSION,
			true
		);

		$section = isset( $_GET['section'] ) ? sanitize_key( wp_unslash( (string) $_GET['section'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only screen check.

		wp_localize_script(
			'wpps-cptb-admin',
			'wppsCptb',
			[
				'dashicons'    => Config::dashicons(),
				'maxKeyLength' => $section === TaxonomyAdmin::SECTION
					? Config::MAX_TAXONOMY_KEY_LENGTH
					: Config::MAX_KEY_LENGTH,
				'strings'      => [
					'confirmDelete'    => __( 'Delete this post type? Existing content will remain in the database but will no longer be accessible until the type is re-registered.', 'wp-powersuite' ),
					'confirmTaxDelete' => __( 'Delete this taxonomy? Existing terms will remain in the database but will no longer be accessible until the taxonomy is re-registered.', 'wp-powersuite' ),
				],
			]
		);
	}
}
