<?php
defined( 'ABSPATH' ) || exit;

/**
 * WooCommerce bootstrap + demo-catalog seeding for Starter Site kits.
 *
 * A kit that sets "requires_woocommerce": true triggers, during import:
 *   1. ensure_woocommerce()  — install (from wordpress.org) + activate WC if missing,
 *                              then run WC's page/option installer.
 *   2. seed_products()       — create real WC_Product objects (categories, tags,
 *                              attributes, images, gallery, price, stock, description)
 *                              from the kit's `products` array. Idempotent by SKU.
 *   3. configure()           — currency, permalinks, and Shop/Cart/Checkout wiring so
 *                              products show under Products → All Products and on Shop.
 *
 * All heavy WP-admin includes are loaded lazily and only when needed.
 */
class Jalpi_Woo {

	const PLUGIN_SLUG = 'woocommerce';
	const PLUGIN_FILE = 'woocommerce/woocommerce.php';

	/** True when WooCommerce is active in this request. */
	public static function is_active(): bool {
		return class_exists( 'WooCommerce' );
	}

	/**
	 * Ensure WooCommerce is installed AND active. Returns [ok, message, installed].
	 *
	 * @return array{ok:bool,message:string,installed:bool,activated:bool}
	 */
	public static function ensure_woocommerce(): array {
		$installed = false;
		$activated = false;

		if ( self::is_active() ) {
			return [ 'ok' => true, 'message' => 'WooCommerce already active.', 'installed' => false, 'activated' => false ];
		}

		if ( ! current_user_can( 'install_plugins' ) || ! current_user_can( 'activate_plugins' ) ) {
			return [ 'ok' => false, 'message' => __( 'You do not have permission to install plugins.', 'jalpi-page-builder' ), 'installed' => false, 'activated' => false ];
		}

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

		// Install the package if the plugin folder isn't present yet.
		if ( ! self::is_installed() ) {
			$install = self::install_plugin_package();
			if ( is_wp_error( $install ) ) {
				return [ 'ok' => false, 'message' => $install->get_error_message(), 'installed' => false, 'activated' => false ];
			}
			$installed = true;
		}

		// Activate.
		$activate = activate_plugin( self::PLUGIN_FILE, '', false, true );
		if ( is_wp_error( $activate ) ) {
			return [ 'ok' => false, 'message' => $activate->get_error_message(), 'installed' => $installed, 'activated' => false ];
		}
		$activated = true;

		// Load WC now so WC_Product etc. are available for seeding in THIS request.
		if ( ! self::is_active() && defined( 'WP_PLUGIN_DIR' ) && file_exists( WP_PLUGIN_DIR . '/' . self::PLUGIN_FILE ) ) {
			include_once WP_PLUGIN_DIR . '/' . self::PLUGIN_FILE;
		}

		// Run WC's own installer so its pages (shop/cart/checkout/my-account) and
		// core options exist without the setup wizard.
		self::run_wc_install();

		$ok = self::is_active();
		return [
			'ok'        => $ok,
			'message'   => $ok ? 'WooCommerce installed and activated.' : __( 'WooCommerce activated but did not load.', 'jalpi-page-builder' ),
			'installed' => $installed,
			'activated' => $activated,
		];
	}

	private static function is_installed(): bool {
		if ( ! function_exists( 'get_plugins' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}
		return array_key_exists( self::PLUGIN_FILE, get_plugins() );
	}

	/**
	 * Download + unpack WooCommerce from the wordpress.org repository using core
	 * upgrader APIs. Returns true or WP_Error.
	 */
	private static function install_plugin_package() {
		require_once ABSPATH . 'wp-admin/includes/file.php';
		require_once ABSPATH . 'wp-admin/includes/misc.php';
		require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
		require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';

		$api = plugins_api( 'plugin_information', [
			'slug'   => self::PLUGIN_SLUG,
			'fields' => [ 'sections' => false ],
		] );
		if ( is_wp_error( $api ) ) {
			return $api;
		}

		// Silent skin — no admin UI output during a REST request.
		$skin     = new Automatic_Upgrader_Skin();
		$upgrader = new Plugin_Upgrader( $skin );
		$result   = $upgrader->install( $api->download_link );

		if ( is_wp_error( $result ) ) {
			return $result;
		}
		if ( false === $result || null === $result ) {
			return new WP_Error( 'install_failed', __( 'WooCommerce download failed. Check the server can reach wordpress.org.', 'jalpi-page-builder' ) );
		}
		return true;
	}

	/** Run WooCommerce's installer (creates pages + default options). */
	private static function run_wc_install(): void {
		if ( class_exists( 'WC_Install' ) && method_exists( 'WC_Install', 'install' ) ) {
			// Suppress the redirect-to-wizard transient so import stays headless.
			WC_Install::install();
			if ( class_exists( 'WC_Admin_Notices' ) && method_exists( 'WC_Admin_Notices', 'remove_all_notices' ) ) {
				WC_Admin_Notices::remove_all_notices();
			}
			delete_transient( '_wc_activation_redirect' );
			update_option( 'woocommerce_admin_notice_theme_support', 'no' );
		}
	}

	// ── Product seeding ────────────────────────────────────────────────────────

	/**
	 * Seed products from the kit's `products` array. Idempotent by SKU.
	 *
	 * Each product def: {
	 *   name, sku?, description?, short_description?, price, sale_price?,
	 *   categories?:[..names], tags?:[..names], attributes?:{Name:[values]},
	 *   stock_status?:'instock'|'outofstock', featured?:bool,
	 *   image, gallery?:[..urls]
	 * }
	 *
	 * @return array{created:int,skipped:int,ids:int[]}
	 */
	/**
	 * Unpublish demo products that were seeded by a DIFFERENT kit.
	 *
	 * Kits are mutually exclusive site designs, so leaving a previous kit's demo
	 * catalogue published mixes unrelated products into the new store's shop and
	 * [products] grids. Products are moved to `draft` (never deleted) so nothing
	 * the user may have edited is destroyed, and only rows this plugin seeded
	 * (those carrying `_jalpi_kit`) are ever touched.
	 *
	 * @param string $keep_kit Kit id whose products should stay published.
	 * @return int Number of products retired.
	 */
	public static function retire_foreign_products( string $keep_kit ): int {
		if ( ! self::is_active() ) {
			return 0;
		}
		$ids = get_posts( [
			'post_type'      => 'product',
			'post_status'    => [ 'publish', 'pending', 'private' ],
			'numberposts'    => -1,
			'fields'         => 'ids',
			// Runs once during kit import to draft the previous kit's products.
			// Never executes on a front-end request, so the meta_query cost is
			// paid once by an administrator rather than per page view.
			// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- admin-only, one-off during kit import
			'meta_query'     => [
				[ 'key' => '_jalpi_kit', 'compare' => 'EXISTS' ],
				[ 'key' => '_jalpi_kit', 'value' => $keep_kit, 'compare' => '!=' ],
			],
		] );
		foreach ( $ids as $id ) {
			wp_update_post( [ 'ID' => $id, 'post_status' => 'draft' ] );
		}
		return count( $ids );
	}

	public static function seed_products( array $products ): array {
		$out = [ 'created' => 0, 'skipped' => 0, 'ids' => [] ];
		if ( ! self::is_active() || ! class_exists( 'WC_Product_Simple' ) ) {
			return $out;
		}

		require_once ABSPATH . 'wp-admin/includes/media.php';
		require_once ABSPATH . 'wp-admin/includes/file.php';
		require_once ABSPATH . 'wp-admin/includes/image.php';

		foreach ( $products as $p ) {
			if ( ! is_array( $p ) || empty( $p['name'] ) ) {
				continue;
			}

			$sku = isset( $p['sku'] ) ? sanitize_text_field( $p['sku'] ) : '';

			// Skip if a product with this SKU already exists (idempotent re-import).
			if ( $sku && function_exists( 'wc_get_product_id_by_sku' ) ) {
				$existing = wc_get_product_id_by_sku( $sku );
				if ( $existing ) {
					$out['skipped']++;
					$out['ids'][] = $existing;
					continue;
				}
			}

			$product = new WC_Product_Simple();
			$product->set_name( sanitize_text_field( $p['name'] ) );
			$product->set_status( 'publish' );
			$product->set_catalog_visibility( 'visible' );

			if ( $sku ) {
				$product->set_sku( $sku );
			}
			if ( isset( $p['description'] ) ) {
				$product->set_description( wp_kses_post( $p['description'] ) );
			}
			if ( isset( $p['short_description'] ) ) {
				$product->set_short_description( wp_kses_post( $p['short_description'] ) );
			}

			$price = isset( $p['price'] ) ? self::num( $p['price'] ) : '';
			if ( '' !== $price ) {
				$product->set_regular_price( $price );
			}
			if ( ! empty( $p['sale_price'] ) ) {
				$sale = self::num( $p['sale_price'] );
				if ( '' !== $sale ) {
					$product->set_sale_price( $sale );
				}
			}

			$product->set_manage_stock( false );
			$product->set_stock_status(
				( ( $p['stock_status'] ?? 'instock' ) === 'outofstock' ) ? 'outofstock' : 'instock'
			);

			if ( ! empty( $p['featured'] ) ) {
				$product->set_featured( true );
			}

			// Categories & tags (create terms as needed).
			if ( ! empty( $p['categories'] ) && is_array( $p['categories'] ) ) {
				$product->set_category_ids( self::term_ids( $p['categories'], 'product_cat' ) );
			}
			if ( ! empty( $p['tags'] ) && is_array( $p['tags'] ) ) {
				$product->set_tag_ids( self::term_ids( $p['tags'], 'product_tag' ) );
			}

			// Attributes.
			if ( ! empty( $p['attributes'] ) && is_array( $p['attributes'] ) ) {
				$attrs = [];
				$pos   = 0;
				foreach ( $p['attributes'] as $name => $values ) {
					$values = array_values( array_filter( array_map( 'sanitize_text_field', (array) $values ) ) );
					if ( ! $values ) {
						continue;
					}
					$attribute = new WC_Product_Attribute();
					$attribute->set_id( 0 ); // custom (non-taxonomy) attribute
					$attribute->set_name( sanitize_text_field( (string) $name ) );
					$attribute->set_options( $values );
					$attribute->set_position( $pos++ );
					$attribute->set_visible( true );
					$attribute->set_variation( false );
					$attrs[] = $attribute;
				}
				if ( $attrs ) {
					$product->set_attributes( $attrs );
				}
			}

			$product_id = $product->save();
			if ( ! $product_id ) {
				continue;
			}

			// Stamp the owning kit so a later import of a DIFFERENT kit can retire
			// this catalogue instead of leaving both kits' products mixed together.
			$owner_kit = (string) get_option( 'jalpi_active_kit', '' );
			if ( $owner_kit !== '' ) {
				update_post_meta( $product_id, '_jalpi_kit', $owner_kit );
			}

			// Featured image + gallery (sideload from URLs). Failures are non-fatal.
			if ( ! empty( $p['image'] ) ) {
				$thumb_id = self::sideload_image( $p['image'], $product_id, $p['name'] );
				if ( $thumb_id ) {
					set_post_thumbnail( $product_id, $thumb_id );
				}
			}
			if ( ! empty( $p['gallery'] ) && is_array( $p['gallery'] ) ) {
				$gallery_ids = [];
				foreach ( $p['gallery'] as $g ) {
					$gid = self::sideload_image( $g, $product_id, $p['name'] );
					if ( $gid ) {
						$gallery_ids[] = $gid;
					}
				}
				if ( $gallery_ids ) {
					update_post_meta( $product_id, '_product_image_gallery', implode( ',', $gallery_ids ) );
				}
			}

			$out['created']++;
			$out['ids'][] = $product_id;
		}

		// Make sure product visibility lookup tables are fresh so the Shop shows them.
		if ( class_exists( 'WC_Post_Data' ) && function_exists( 'wc_delete_product_transients' ) ) {
			wc_delete_product_transients();
		}

		return $out;
	}

	/** Resolve/create a list of term names → term IDs for a taxonomy. */
	private static function term_ids( array $names, string $taxonomy ): array {
		$ids = [];
		foreach ( $names as $name ) {
			$name = sanitize_text_field( $name );
			if ( '' === $name ) {
				continue;
			}
			$term = get_term_by( 'name', $name, $taxonomy );
			if ( ! $term ) {
				$new = wp_insert_term( $name, $taxonomy );
				if ( ! is_wp_error( $new ) ) {
					$ids[] = (int) $new['term_id'];
				}
			} else {
				$ids[] = (int) $term->term_id;
			}
		}
		return $ids;
	}

	/** Download a remote image into the media library, attached to a product. */
	private static function sideload_image( string $url, int $post_id, string $desc ): int {
		$url = esc_url_raw( $url );
		if ( '' === $url ) {
			return 0;
		}
		$tmp = download_url( $url, 30 );
		if ( is_wp_error( $tmp ) ) {
			return 0;
		}
		// Give the temp file a sane extension so WP accepts it (Unsplash URLs are extensionless).
		$file_array = [
			'name'     => sanitize_title( $desc ) . '-' . wp_generate_password( 6, false ) . '.jpg',
			'tmp_name' => $tmp,
		];
		$id = media_handle_sideload( $file_array, $post_id, $desc );
		if ( is_wp_error( $id ) ) {
			// phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
			@unlink( $tmp );
			return 0;
		}
		return (int) $id;
	}

	private static function num( $v ): string {
		$v = preg_replace( '/[^0-9.]/', '', (string) $v );
		return ( '' === $v ) ? '' : $v;
	}

	// ── Store configuration ────────────────────────────────────────────────────

	/** Currency + permalinks + launch state. Safe to call repeatedly. */
	public static function configure( array $cfg = [] ): void {
		if ( ! self::is_active() ) {
			return;
		}
		$currency = strtoupper( sanitize_text_field( $cfg['currency'] ?? 'USD' ) );
		update_option( 'woocommerce_currency', $currency );
		update_option( 'woocommerce_default_country', sanitize_text_field( $cfg['country'] ?? 'US:CA' ) );

		// ── Launch the store immediately ─────────────────────────────────────
		// WC 8.x+ ("Launch Your Store") defaults new installs to a "Coming soon"
		// page that HIDES the storefront and all products. Turn it off so the
		// shop and products are live the moment the kit finishes importing.
		update_option( 'woocommerce_coming_soon', 'no' );
		update_option( 'woocommerce_store_pages_only', 'no' );

		// Mark onboarding/setup as complete so no wizard or task-list nag appears.
		update_option( 'woocommerce_onboarding_profile', array_merge(
			(array) get_option( 'woocommerce_onboarding_profile', [] ),
			[ 'completed' => true, 'skipped' => true ]
		) );
		update_option( 'woocommerce_task_list_hidden', 'yes' );
		update_option( 'woocommerce_task_list_welcome_modal_dismissed', 'yes' );
		if ( class_exists( '\Automattic\WooCommerce\Admin\Features\Onboarding' ) ) {
			update_option( 'woocommerce_onboarding_profile', [ 'completed' => true, 'skipped' => true ] );
		}

		// Product permalinks (/product/…) — flush so links work immediately.
		if ( function_exists( 'flush_rewrite_rules' ) ) {
			flush_rewrite_rules( false );
		}
	}

	/** Front-end permalink of the WooCommerce shop page (empty if unset). */
	public static function shop_url(): string {
		$id = (int) get_option( 'woocommerce_shop_page_id' );
		return $id ? (string) get_permalink( $id ) : '';
	}

	// ── Front-end integration (runs every request when WC is active) ───────────

	/**
	 * Registered from jalpi_init(). Only does anything once WooCommerce is loaded,
	 * so it is safe to always attach.
	 */
	public static function init_frontend(): void {
		add_action( 'wp_enqueue_scripts', [ __CLASS__, 'enqueue_on_wc_pages' ], 20 );
		// woocommerce_content() (used by our template takeover) fires
		// woocommerce_archive_description before the product loop — hook there so
		// the LUXE hero appears above the grid on the shop/category archive.
		add_action( 'woocommerce_archive_description', [ __CLASS__, 'maybe_shop_hero' ], 5 );
		// Belt-and-braces for themes/templates that render the native WC wrapper.
		add_action( 'woocommerce_before_main_content', [ __CLASS__, 'maybe_shop_hero' ], 5 );

		// Live header cart count: any element with class .jalpi-cart-count is kept
		// in sync with the real WooCommerce cart. Two parts:
		//  1. On every page load, set the initial count (the header is static HTML
		//     rendered by the template, so the number is written client-side).
		//  2. WC's add-to-cart AJAX fragments refresh it instantly without reload.
		add_action( 'wp_footer', [ __CLASS__, 'cart_count_sync_script' ], 30 );
		add_filter( 'woocommerce_add_to_cart_fragments', [ __CLASS__, 'cart_count_fragment' ] );

		// Emit a `jalpi-kit-<id>` body class so kit-specific theming (light PULSE
		// vs. dark LUXE product cards, etc.) can be scoped and never bleed across
		// kits. Applies to the front-end template takeover (uses body_class()).
		add_filter( 'body_class', [ __CLASS__, 'add_active_kit_body_class' ] );
	}

	/** Append `jalpi-kit-<active kit id>` to <body> when a kit is active. */
	public static function add_active_kit_body_class( array $classes ): array {
		$kit = get_option( 'jalpi_active_kit', '' );
		if ( is_string( $kit ) && $kit !== '' ) {
			$classes[] = 'jalpi-kit-' . sanitize_html_class( $kit );
		}
		return $classes;
	}

	/** Current cart item count (0 when WC/cart unavailable). */
	public static function cart_count(): int {
		if ( self::is_active() && function_exists( 'WC' ) && WC()->cart ) {
			return (int) WC()->cart->get_cart_contents_count();
		}
		return 0;
	}

	/**
	 * WC AJAX fragment: replace the innards of every .jalpi-cart-count with the
	 * live count so adding to cart updates the header badge instantly.
	 */
	public static function cart_count_fragment( array $fragments ): array {
		$count = self::cart_count();
		// WC fragments are keyed by a CSS selector; the value replaces the OUTER
		// html of the FIRST match. We wrap the number so the badge keeps its style.
		$fragments['span.jalpi-cart-count'] =
			'<span class="jalpi-cart-count" data-count="' . esc_attr( (string) $count ) . '">' . esc_html( (string) $count ) . '</span>';
		return $fragments;
	}

	/** Inline script: set the initial header cart count on page load. */
	public static function cart_count_sync_script(): void {
		if ( ! self::is_active() ) {
			return;
		}
		$count = self::cart_count();
		?>
<script>
(function(){
  function setCount(n){
    document.querySelectorAll('.jalpi-cart-count').forEach(function(el){
      el.textContent = n;
      el.setAttribute('data-count', n);
      el.style.display = (parseInt(n,10) > 0) ? '' : 'none';
    });
  }
  setCount(<?php echo (int) $count; ?>);
  // Keep in sync when WooCommerce updates cart fragments (add/remove to cart).
  if (window.jQuery) {
    jQuery(document.body).on('added_to_cart removed_from_cart wc_fragments_refreshed wc_fragments_loaded', function(){
      if (window.wc_cart_fragments_params) {
        try {
          var f = JSON.parse(sessionStorage.getItem(wc_cart_fragments_params.fragment_name) || '{}');
          if (f && f['span.jalpi-cart-count']) {
            var tmp = document.createElement('div'); tmp.innerHTML = f['span.jalpi-cart-count'];
            var src = tmp.querySelector('.jalpi-cart-count');
            if (src) setCount(src.getAttribute('data-count') || src.textContent.trim());
          }
        } catch(e){}
      }
    });
  }
})();
</script>
		<?php
	}

	/**
	 * Ensure the Jalpi frontend stylesheet (which carries the LUXE WooCommerce
	 * theming) is present on WooCommerce pages, even when no Jalpi canvas is on
	 * the page (e.g. the native shop archive / single product).
	 */
	public static function enqueue_on_wc_pages(): void {
		if ( ! self::is_active() || ! function_exists( 'is_woocommerce' ) ) {
			return;
		}
		if ( is_woocommerce() || is_cart() || is_checkout() || is_account_page() ) {
			if ( ! wp_style_is( 'jalpi-frontend', 'registered' ) ) {
				wp_register_style( 'jalpi-frontend', JALPI_ASSETS_URL . 'css/frontend.css', [], Jalpi_Renderer::frontend_css_ver() );
			}
			wp_enqueue_style( 'jalpi-frontend' );
			// Fonts used by the LUXE theme.
			wp_enqueue_style(
				'jalpi-luxe-fonts',
				'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=Playfair+Display:ital,wght@0,400;0,600;0,700;1,400;1,600&display=swap',
				[],
				JALPI_VERSION
			);
		}
	}

	/**
	 * Inject a LUXE hero above the shop archive product grid. Only on the main
	 * shop / product-taxonomy archive, and only when a Jalpi header template is
	 * active (so it matches an imported kit, never a plain WC site).
	 */
	public static function maybe_shop_hero(): void {
		static $done = false;
		if ( $done ) {
			return; // both hooks are attached; only render once
		}
		if ( ! self::is_active() || ! function_exists( 'is_shop' ) ) {
			return;
		}
		if ( ! ( is_shop() || is_product_taxonomy() ) ) {
			return;
		}
		$done = true;
		// Only when a Jalpi header template is active (imported kit context).
		if ( ! class_exists( 'Jalpi_Theme_Builder' )
			|| ! Jalpi_Theme_Builder::instance()->find_template( 'header' ) ) {
			return;
		}

		$kit = (string) get_option( 'jalpi_active_kit', '' );

		if ( 'pulse' === $kit ) {
			$eyebrow  = __( 'The Drop', 'jalpi-page-builder' );
			$title    = is_shop() ? __( 'Shop everything', 'jalpi-page-builder' ) : single_term_title( '', false );
			$sub      = __( 'Fresh fleece, tapered cargos and chunky runners — new drops every Friday.', 'jalpi-page-builder' );
			$mod      = ' jalpi-woo-hero--pulse';
		} else {
			$eyebrow  = __( 'LUXE Maison', 'jalpi-page-builder' );
			$title    = is_shop() ? __( 'The Collection', 'jalpi-page-builder' ) : single_term_title( '', false );
			$sub      = __( 'Browse the complete catalogue — crafted objects, each guaranteed for a lifetime.', 'jalpi-page-builder' );
			$mod      = '';
		}

		echo '<div class="jalpi-woo-hero' . esc_attr( $mod ) . '">'
			. '<div class="jalpi-woo-hero__eyebrow">' . esc_html( $eyebrow ) . '</div>'
			. '<h1 class="jalpi-woo-hero__title">' . esc_html( $title ) . '</h1>'
			. '<p class="jalpi-woo-hero__sub">' . esc_html( $sub ) . '</p>'
			. '</div>';
	}
}
