<?php
/**
 * WooCommerce admin UI for price label PDF generation (wordpress.org / free distribution).
 *
 * @package PhoenixWP\PriceLabels
 */

declare(strict_types=1);

namespace PhoenixWP\PriceLabels\Admin;

use PhoenixWP\PriceLabels\Install;
use PhoenixWP\PriceLabels\Labels\Label_Data_Builder;
use PhoenixWP\PriceLabels\Labels\Label_Field_Settings;
use PhoenixWP\PriceLabels\Labels\Variation_Resolver;
use PhoenixWP\PriceLabels\Pdf\Pdf_Renderer;
use PhoenixWP\PriceLabels\Pdf\Product_Overview_Renderer;
use PhoenixWP\PriceLabels\Pdf\Sheet_Layout;

defined( 'ABSPATH' ) || exit;

/**
 * Admin screen and PDF download handler for the free plugin distribution.
 */
final class Labels_Admin {

	private static ?self $instance = null;

	public const CORE_MENU_SLUG    = 'phoenix-wp-core';
	public const PAGE_SLUG         = 'phoenix-price-labels-for-woocommerce';
	public const MENU_POSITION     = 59;
	public const ACTION_GENERATE   = 'phoenix_wp_price_labels_generate';
	public const OUTPUT_LABELS     = 'labels';
	public const OUTPUT_OVERVIEW   = 'overview';

	public static function instance(): self {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}

		return self::$instance;
	}

	private function __construct() {}

	private function __clone() {}

	/**
	 * Registers admin hooks.
	 */
	public function init(): void {
		add_action( 'admin_menu', array( $this, 'register_menu' ), 25 );
		add_action( 'admin_post_' . self::ACTION_GENERATE, array( $this, 'handle_generate' ) );
		Assets::instance()->init();
		Product_Search::instance()->init();
		Unit_Price_Settings_Admin::instance()->init();
		Label_Field_Settings_Admin::instance()->init();
	}

	/**
	 * Adds admin menu: top-level without Core, submenu under PhoenixWP with Core.
	 */
	public function register_menu(): void {
		$page_title = phoenix_wp_price_labels_get_admin_page_title();
		$menu_title = phoenix_wp_price_labels_get_admin_menu_title();
		$capability = 'manage_woocommerce';
		$callback   = array( $this, 'render_page' );

		if ( phoenix_wp_price_labels_is_core_active() ) {
			add_submenu_page(
				self::CORE_MENU_SLUG,
				$page_title,
				$page_title,
				$capability,
				self::PAGE_SLUG,
				$callback
			);
			return;
		}

		add_menu_page(
			$menu_title,
			$menu_title,
			$capability,
			self::PAGE_SLUG,
			$callback,
			'dashicons-tag',
			self::MENU_POSITION
		);
	}

	/**
	 * Renders label generator screen.
	 */
	public function render_page(): void {
		if ( ! current_user_can( 'manage_woocommerce' ) ) {
			return;
		}

		echo '<div class="wrap">';
		echo '<h1>' . esc_html( phoenix_wp_price_labels_get_plugin_display_name() ) . '</h1>';
		echo '<p>' . esc_html__( 'Generate offline PDF price labels for Avery sheets and office printers.', 'phoenix-price-labels-for-woocommerce' ) . '</p>';

		$saved = $this->get_sanitized_get_param( 'phoenix_wp_price_labels_saved' );
		if ( 'unit_price' === $saved ) {
			echo '<div class="notice notice-success is-dismissible"><p>';
			echo esc_html__( 'Unit price settings saved.', 'phoenix-price-labels-for-woocommerce' );
			echo '</p></div>';
		}

		if ( ! Install::requirements_met() ) {
			echo '<div class="notice notice-error inline"><p>';
			echo esc_html__( 'WooCommerce must be active before you can generate price labels.', 'phoenix-price-labels-for-woocommerce' );
			echo '</p></div></div>';
			return;
		}

		$error = $this->get_sanitized_get_param( 'phoenix_wp_price_labels_error' );
		if ( '' !== $error ) {
			if ( 'no_products' === $error ) {
				echo '<div class="notice notice-error inline phoenix-price-labels-for-woocommerce-admin-error"><p>' . esc_html__( 'Select products or enter product IDs.', 'phoenix-price-labels-for-woocommerce' ) . '</p></div>';
			} elseif ( 'no_labels' === $error ) {
				echo '<div class="notice notice-error inline phoenix-price-labels-for-woocommerce-admin-error"><p>' . esc_html__( 'No printable labels found for the selected products.', 'phoenix-price-labels-for-woocommerce' ) . '</p></div>';
			}
		}

		if ( ! is_readable( PHOENIX_PRICE_LABELS_FOR_WOOCOMMERCE_PATH . 'vendor/autoload.php' ) ) {
			echo '<div class="notice notice-error inline"><p>';
			echo esc_html__( 'Composer dependencies are missing. Run composer install in the plugin folder before generating PDFs.', 'phoenix-price-labels-for-woocommerce' );
			echo '</p></div></div>';
			return;
		}

		$form_state = Generator_Form_State::get_for_current_user();

		echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '">';
		wp_nonce_field( self::ACTION_GENERATE, '_phoenix_wp_price_labels_generate_nonce' );
		echo '<input type="hidden" name="action" value="' . esc_attr( self::ACTION_GENERATE ) . '" />';

		echo '<h2>' . esc_html__( 'Select products', 'phoenix-price-labels-for-woocommerce' ) . '</h2>';
		$this->render_product_picker( $form_state );

		echo '<table class="form-table" role="presentation">';
		echo '<tr><th scope="row"><label for="phoenix_wp_price_labels_product_ids">' . esc_html__( 'Product IDs (optional)', 'phoenix-price-labels-for-woocommerce' ) . '</label></th>';
		echo '<td><textarea name="product_ids" id="phoenix_wp_price_labels_product_ids" rows="3" cols="50" class="large-text" placeholder="123, 456, 789">';
		echo esc_textarea( (string) ( $form_state['product_ids'] ?? '' ) );
		echo '</textarea>';
		echo '<p class="description">' . esc_html__( 'Optional fallback: comma-separated product or variation IDs. Variable products expand to all variations.', 'phoenix-price-labels-for-woocommerce' ) . '</p></td></tr>';

		echo '<tr><th scope="row">' . esc_html__( 'Output', 'phoenix-price-labels-for-woocommerce' ) . '</th><td>';
		echo '<fieldset>';
		echo '<label><input type="radio" name="output_type" value="' . esc_attr( self::OUTPUT_LABELS ) . '" checked /> ';
		echo esc_html__( 'Label sheet', 'phoenix-price-labels-for-woocommerce' ) . '</label><br />';
		echo '<label><input type="radio" name="output_type" value="' . esc_attr( self::OUTPUT_OVERVIEW ) . '" /> ';
		echo esc_html__( 'Product overview', 'phoenix-price-labels-for-woocommerce' ) . '</label>';
		echo '<p class="description">' . esc_html__( 'Overview: printable A4 list for the checkout — for products without shelf labels.', 'phoenix-price-labels-for-woocommerce' ) . '</p>';
		echo '</fieldset></td></tr>';

		echo '<tr class="phoenix-price-labels-for-woocommerce-layout-row"><th scope="row">' . esc_html__( 'Sheet layout', 'phoenix-price-labels-for-woocommerce' ) . '</th><td>';
		echo '<select name="layout" id="phoenix_wp_price_labels_layout">';
		$this->render_layout_options( Sheet_Layout::get_avery_presets(), __( 'Avery', 'phoenix-price-labels-for-woocommerce' ) );
		$this->render_layout_options( Sheet_Layout::get_plain_paper_presets(), __( 'Plain paper', 'phoenix-price-labels-for-woocommerce' ) );
		echo '</select>';
		echo '</td></tr>';

		Label_Field_Settings_Admin::instance()->render_fields_row();

		echo '</table>';

		echo '<p class="submit"><button type="submit" class="button button-primary" name="action" value="' . esc_attr( self::ACTION_GENERATE ) . '">' . esc_html__( 'Download PDF', 'phoenix-price-labels-for-woocommerce' ) . '</button></p>';
		echo '</form>';
		echo '</div>';
	}

	/**
	 * Builds label data and streams PDF download.
	 */
	public function handle_generate(): void {
		if ( ! current_user_can( 'manage_woocommerce' ) ) {
			wp_die( esc_html__( 'Permission denied.', 'phoenix-price-labels-for-woocommerce' ) );
		}

		check_admin_referer( self::ACTION_GENERATE, '_phoenix_wp_price_labels_generate_nonce' );

		if ( ! is_readable( PHOENIX_PRICE_LABELS_FOR_WOOCOMMERCE_PATH . 'vendor/autoload.php' ) ) {
			wp_die( esc_html__( 'PDF library missing.', 'phoenix-price-labels-for-woocommerce' ) );
		}

		$request = filter_input_array( INPUT_POST, FILTER_UNSAFE_RAW );
		if ( ! is_array( $request ) ) {
			$request = array();
		}

		Generator_Form_State::capture_from_request( $request );

		$labels = $this->build_labels_from_request( $request );
		if ( null === $labels ) {
			exit;
		}

		$output_type = isset( $request['output_type'] ) ? sanitize_key( (string) $request['output_type'] ) : self::OUTPUT_LABELS;
		$date_stamp  = gmdate( 'Y-m-d' );
		$render_opts = $this->build_render_options_from_request( $request );

		if ( self::OUTPUT_OVERVIEW === $output_type ) {
			( new Product_Overview_Renderer() )->stream(
				$labels,
				'phoenix-price-labels-for-woocommerce-overview-' . $date_stamp . '.pdf',
				$render_opts
			);
			exit;
		}

		$layout_key = isset( $request['layout'] ) ? sanitize_key( (string) $request['layout'] ) : Sheet_Layout::PRESET_L7159;
		if ( ! in_array( $layout_key, Sheet_Layout::get_allowed_preset_keys(), true ) ) {
			$layout_key = Sheet_Layout::PRESET_L7159;
		}

		( new Pdf_Renderer( new Sheet_Layout( $layout_key ) ) )->stream(
			$labels,
			'phoenix-price-labels-for-woocommerce-' . $date_stamp . '.pdf',
			$render_opts
		);
	}

	/**
	 * @param array<string, mixed> $request Sanitized admin POST payload (nonce already verified).
	 *
	 * @return array<string, mixed>
	 */
	private function build_render_options_from_request( array $request ): array {
		$field_input = isset( $request['label_fields'] ) && is_array( $request['label_fields'] )
			? Label_Field_Settings::sanitize( $request['label_fields'] )
			: array();

		return array(
			'visible_fields' => Label_Field_Settings::resolve_for_run( $field_input ),
			'style'          => array(),
		);
	}

	/**
	 * @param array<string, mixed> $request Sanitized admin POST payload (nonce already verified).
	 *
	 * @return array<int, array<string, mixed>>|null Null when redirecting after validation error.
	 */
	private function build_labels_from_request( array $request ): ?array {
		$raw_ids = isset( $request['product_ids'] ) ? sanitize_textarea_field( (string) $request['product_ids'] ) : '';
		$picker  = isset( $request['picker_product_ids'] ) ? sanitize_text_field( (string) $request['picker_product_ids'] ) : '';
		$ids     = $this->collect_product_ids( $picker, $raw_ids );

		if ( array() === $ids ) {
			wp_safe_redirect( add_query_arg( 'phoenix_wp_price_labels_error', 'no_products', admin_url( 'admin.php?page=' . self::PAGE_SLUG ) ) );
			return null;
		}

		$products = ( new Variation_Resolver() )->resolve( $ids, true );
		$builder  = new Label_Data_Builder();
		$labels   = array();

		foreach ( $products as $product ) {
			$labels[] = $builder->build( $product );
		}

		if ( array() === $labels ) {
			wp_safe_redirect( add_query_arg( 'phoenix_wp_price_labels_error', 'no_labels', admin_url( 'admin.php?page=' . self::PAGE_SLUG ) ) );
			return null;
		}

		return $labels;
	}

	/**
	 * Renders searchable multi-select product picker.
	 *
	 * @param array<string, mixed> $form_state Persisted picker values.
	 */
	private function render_product_picker( array $form_state = array() ): void {
		$picker_ids = (string) ( $form_state['picker_product_ids'] ?? '' );

		echo '<div class="phoenix-price-labels-for-woocommerce-picker">';
		echo '<input type="search" class="phoenix-price-labels-for-woocommerce-picker__search" id="phoenix-price-labels-for-woocommerce-search" placeholder="' . esc_attr__( 'Search by name, SKU, or product ID…', 'phoenix-price-labels-for-woocommerce' ) . '" autocomplete="off" />';
		printf(
			'<input type="hidden" name="picker_product_ids" id="phoenix_wp_price_labels_picker_ids" value="%1$s" />',
			esc_attr( $picker_ids )
		);
		echo '<div class="phoenix-price-labels-for-woocommerce-picker__list" id="phoenix-price-labels-for-woocommerce-product-list"></div>';
		echo '<p class="phoenix-price-labels-for-woocommerce-picker__meta">';
		echo esc_html__( 'Selected:', 'phoenix-price-labels-for-woocommerce' ) . ' ';
		echo '<span class="phoenix-price-labels-for-woocommerce-picker__selected-count">0</span> ';
		echo esc_html__( 'products', 'phoenix-price-labels-for-woocommerce' );
		echo '</p>';
		echo '<p class="phoenix-price-labels-for-woocommerce-picker__actions">';
		echo '<button type="button" class="button phoenix-price-labels-for-woocommerce-picker__load-more" style="display:none;">' . esc_html__( 'Load more', 'phoenix-price-labels-for-woocommerce' ) . '</button>';
		echo '</p>';
		echo '</div>';
	}

	/**
	 * Merges picker and manual product ID inputs.
	 *
	 * @return int[]
	 */
	private function collect_product_ids( string $picker_ids, string $manual_ids ): array {
		$merged = array_merge(
			array_filter( array_map( 'absint', preg_split( '/[\s,;]+/', $picker_ids ) ?: array() ) ),
			array_filter( array_map( 'absint', preg_split( '/[\s,;]+/', $manual_ids ) ?: array() ) )
		);

		return array_values( array_unique( $merged ) );
	}

	/**
	 * @param string[] $presets     Preset keys.
	 * @param string   $group_label Optgroup label.
	 */
	private function render_layout_options( array $presets, string $group_label ): void {
		if ( array() === $presets ) {
			return;
		}

		echo '<optgroup label="' . esc_attr( $group_label ) . '">';
		foreach ( $presets as $preset ) {
			$option_layout = new Sheet_Layout( $preset );
			$dimensions    = $option_layout->get_dimensions();
			$label_height  = isset( $dimensions['label_height'] ) ? (float) $dimensions['label_height'] : 0.0;

			printf(
				'<option value="%1$s" data-label-height="%2$s">%3$s</option>',
				esc_attr( $preset ),
				esc_attr( (string) $label_height ),
				esc_html( $option_layout->get_name() )
			);
		}
		echo '</optgroup>';
	}

	/**
	 * Reads a sanitized admin query flag (PRG redirect notices).
	 *
	 * @param string $key Query parameter name.
	 */
	private function get_sanitized_get_param( string $key ): string {
		$value = filter_input( INPUT_GET, $key, FILTER_DEFAULT );

		if ( ! is_string( $value ) || '' === $value ) {
			return '';
		}

		return sanitize_key( $value );
	}
}
