<?php

namespace AIEO\Claude\Abilities;

defined( 'ABSPATH' ) || exit;

class StockRecoveryAbility {

	const ABILITY_NAME = 'aieo/stock-recovery';

	public static function register() {
		wp_register_ability(
			self::ABILITY_NAME,
			array(
				'label'               => __( 'Stock recovery (false-OOS)', 'ai-eshop-optimizer' ),
				'description'         => __( 'Products the ERP marks OUT OF STOCK in the eshop that the PHYSICAL stores are still selling — strong evidence the eshop OOS is wrong. Returns candidates with days_oos, physical units sold + distinct sale-days since the OOS date, suggested restock qty, and meets_threshold (whether they clear the manager\'s auto-restock confidence rules). Also returns a recovered-sales summary: eshop units + € turnover earned after each restock, split auto vs total, and a precision % (success vs failure of restocks). Reads wp_aieo_dmm_stock_recovery_candidates + _events, built by the Catalog Sync → "Stock recovery" job.', 'ai-eshop-optimizer' ),
				'category'            => \AIEO\Claude\Bootstrap::CATEGORY,
				'input_schema'        => array(
					'type'       => 'object',
					'properties' => array(
						'only_meets_threshold' => array(
							'type'        => 'boolean',
							'default'     => false,
							'description' => 'Only return candidates that clear the auto-restock thresholds (the ones that would be auto-applied).',
						),
						'limit' => array(
							'type'    => 'integer',
							'minimum' => 1,
							'maximum' => 200,
							'default' => 50,
						),
					),
				),
				'output_schema'       => array( 'type' => 'object' ),
				'execute_callback'    => array( __CLASS__, 'execute' ),
				'permission_callback' => array( __CLASS__, 'permission_check' ),
				'meta'                => array(
					'annotations'  => array( 'readonly' => true, 'idempotent' => true ),
					'show_in_rest' => true,
				),
			)
		);
	}

	public static function permission_check( $input = null ) {
		unset( $input );
		return current_user_can( 'manage_woocommerce' );
	}

	public static function execute( $input ) {
		global $wpdb;
		$input = is_array( $input ) ? $input : array();
		$only  = ! empty( $input['only_meets_threshold'] );
		$limit = isset( $input['limit'] ) ? max( 1, min( 200, (int) $input['limit'] ) ) : 50;

		$cand = $wpdb->prefix . 'aieo_dmm_stock_recovery_candidates';
		$ev   = $wpdb->prefix . 'aieo_dmm_stock_recovery_events';
		if ( (string) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $cand ) ) !== $cand ) {
			return new \WP_Error( 'aieo_sr_missing', __( 'Run the "Stock recovery" job under Catalog Sync first.', 'ai-eshop-optimizer' ) );
		}

		$c = $wpdb->get_row( "SELECT COUNT(*) total, SUM(meets_threshold=1) meets,
		                             ROUND(SUM(est_lost_sales),2) est_lost, ROUND(SUM(phys_turnover_after),2) instore
		                      FROM `{$cand}` WHERE phys_units_after > 0", ARRAY_A );

		$e = array();
		if ( (string) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $ev ) ) === $ev ) {
			$e = $wpdb->get_row(
				"SELECT COUNT(*) applied, SUM(mode='auto') autos,
				        ROUND(SUM(eshop_units_recovered),0) units, ROUND(SUM(eshop_turnover_recovered),2) turnover,
				        ROUND(SUM(CASE WHEN mode='auto' THEN eshop_turnover_recovered ELSE 0 END),2) auto_turnover,
				        SUM(outcome='success') successes, SUM(outcome='failure') failures
				 FROM `{$ev}`", ARRAY_A );
		}
		$succ = (int) ( $e['successes'] ?? 0 );
		$fail = (int) ( $e['failures'] ?? 0 );

		$where = 'phys_units_after > 0 AND status <> \'dismissed\'' . ( $only ? ' AND meets_threshold = 1' : '' );
		$rows  = $wpdb->get_results(
			"SELECT product_id, sku, name, days_oos, oos_source, phys_units_after, phys_sale_days,
			        last_phys_sale_date, phys_turnover_after,
			        est_lost_sales, lost_optimistic, lost_pessimistic, lost_average,
			        suggested_restock_qty, meets_threshold, status, blocked_reason
			 FROM `{$cand}` WHERE {$where}
			 ORDER BY meets_threshold DESC, phys_units_after DESC, last_phys_sale_date DESC
			 LIMIT {$limit}", ARRAY_A );

		return array(
			'summary'  => array(
				'candidates_selling_in_stores' => (int) ( $c['total'] ?? 0 ),
				'meet_threshold'               => (int) ( $c['meets'] ?? 0 ),
				'estimated_lost_online_sales'  => (float) ( $c['est_lost'] ?? 0 ),
				'in_store_turnover_same_period' => (float) ( $c['instore'] ?? 0 ),
				'restocks_applied'             => (int) ( $e['applied'] ?? 0 ),
				'restocks_auto'                => (int) ( $e['autos'] ?? 0 ),
				'units_recovered'              => (float) ( $e['units'] ?? 0 ),
				'turnover_recovered'           => (float) ( $e['turnover'] ?? 0 ),
				'turnover_recovered_via_auto'  => (float) ( $e['auto_turnover'] ?? 0 ),
				'precision_pct'                => ( $succ + $fail ) > 0 ? round( 100 * $succ / ( $succ + $fail ), 1 ) : null,
			),
			'note'     => __( 'days_oos = how long the eshop has shown it OOS (oos_source: history=precise, latest_sale=estimated from last online sale). meets_threshold=1 means it clears the manager\'s auto-restock rules. est_lost_sales = the sales predictor\'s AVERAGE modelled lost-online-sales cost while OOS (lost_pessimistic..lost_optimistic = the range); estimated_lost_online_sales sums it. phys_turnover_after / in_store_turnover_same_period = the ACTUAL in-store net € for the same period.', 'ai-eshop-optimizer' ),
			'count'    => count( (array) $rows ),
			'products' => array_values( (array) $rows ),
		);
	}
}
