<?php
/**
 * Category-aware scoring module.
 *
 * @package TrustLens\Modules
 * @since   1.0.0
 */

defined( 'ABSPATH' ) || exit;

/**
 * Categories module class.
 *
 * Tracks returns by product category and applies category-specific scoring:
 * - High-risk categories (electronics, jewelry) weigh returns more heavily
 * - Low-risk categories (consumables) weigh returns less
 * - Detects category-specific abuse patterns
 *
 * @since 1.0.0
 */
class TrustLens_Module_Categories extends TrustLens_Module {

	/**
	 * Module ID.
	 *
	 * @var string
	 */
	protected string $id = 'categories';

	/**
	 * Module name.
	 *
	 * @var string
	 */
	protected string $name = 'Category-Aware Scoring';

	/**
	 * Module description.
	 *
	 * @var string
	 */
	protected string $description = 'Applies different scoring weights based on product categories.';

	/**
	 * Is Pro feature.
	 *
	 * @var bool
	 */
	protected bool $is_pro = false;

	/**
	 * Default category risk weights.
	 *
	 * Higher weight = returns in this category impact score more.
	 *
	 * @var array
	 */
	private array $default_weights = array(
		'electronics'   => 1.5,
		'jewelry'       => 1.5,
		'clothing'      => 1.0,
		'accessories'   => 1.0,
		'home-garden'   => 0.8,
		'food-grocery'  => 0.5,
		'default'       => 1.0,
	);

	/**
	 * Register hooks.
	 *
	 * @since 1.0.0
	 */
	public function register_hooks(): void {
		// Track category-specific refunds.
		add_action( 'woocommerce_order_refunded', array( $this, 'handle_refund' ), 15, 2 );

		// Track category-specific orders.
		add_action( 'woocommerce_checkout_order_created', array( $this, 'handle_order' ), 15 );
	}

	/**
	 * Handle order - track category-specific data.
	 *
	 * @since 1.0.0
	 * @param WC_Order $order Order object.
	 */
	public function handle_order( WC_Order $order ): void {
		$customer_key = wstl_get_customer_key( $order );
		$this->track_order_categories( $customer_key['email_hash'], $order );
	}

	/**
	 * Handle refund - track category-specific data.
	 *
	 * @since 1.0.0
	 * @param int $order_id  Order ID.
	 * @param int $refund_id Refund ID.
	 */
	public function handle_refund( int $order_id, int $refund_id ): void {
		$order = wc_get_order( $order_id );
		$refund = wc_get_order( $refund_id );

		if ( ! $order || ! $refund ) {
			return;
		}

		$customer_key = wstl_get_customer_key( $order );
		$email_hash = $customer_key['email_hash'];

		// Get refunded items and their categories.
		$refund_items = $refund->get_items();
		$category_refunds = array();

		foreach ( $refund_items as $item ) {
			$product_id = $item->get_product_id();
			$product = wc_get_product( $product_id );

			if ( ! $product ) {
				continue;
			}

			$categories = wp_get_post_terms( $product_id, 'product_cat', array( 'fields' => 'slugs' ) );
			$refund_amount = abs( $item->get_total() );

			foreach ( $categories as $category ) {
				if ( ! isset( $category_refunds[ $category ] ) ) {
					$category_refunds[ $category ] = 0;
				}
				$category_refunds[ $category ] += $refund_amount;
			}
		}

		// Update category refund stats.
		if ( ! empty( $category_refunds ) ) {
			$this->update_category_stats( $email_hash, $category_refunds );

			// Log event.
			$this->log_event(
				$email_hash,
				'category_refund',
				array(
					'order_id'         => $order_id,
					'refund_id'        => $refund_id,
					'category_refunds' => $category_refunds,
				),
				$order_id
			);
		}
	}

	/**
	 * Update customer category refund statistics.
	 *
	 * @since 1.0.0
	 * @param string $email_hash       Customer email hash.
	 * @param array  $category_refunds Category refund amounts.
	 */
	private function update_category_stats( string $email_hash, array $category_refunds ): void {
		global $wpdb;

		$table = $wpdb->prefix . 'trustlens_category_stats';

		foreach ( $category_refunds as $category => $amount ) {
			// Try to update existing record.
			// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix, safe.
			$updated = $wpdb->query( $wpdb->prepare(
				"UPDATE {$table}
				 SET refund_count = refund_count + 1,
				     refund_value = refund_value + %f,
				     updated_at = %s
				 WHERE email_hash = %s AND category_slug = %s",
				$amount,
				current_time( 'mysql' ),
				$email_hash,
				$category
			) );
			// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter

			// Insert if no existing record.
			if ( 0 === $updated ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Table name from $wpdb->prefix, safe.
				$wpdb->insert(
					$table,
					array(
						'email_hash'    => $email_hash,
						'category_slug' => $category,
						'order_count'   => 0,
						'order_value'   => 0,
						'refund_count'  => 1,
						'refund_value'  => $amount,
						'created_at'    => current_time( 'mysql' ),
						'updated_at'    => current_time( 'mysql' ),
					),
					array( '%s', '%s', '%d', '%f', '%d', '%f', '%s', '%s' )
				);
			}
		}
	}

	/**
	 * Track order by category.
	 *
	 * @since 1.0.0
	 * @param string   $email_hash Customer email hash.
	 * @param WC_Order $order      Order object.
	 */
	public function track_order_categories( string $email_hash, WC_Order $order ): void {
		global $wpdb;

		$table = $wpdb->prefix . 'trustlens_category_stats';
		$category_orders = array();

		foreach ( $order->get_items() as $item ) {
			$product_id = $item->get_product_id();
			$categories = wp_get_post_terms( $product_id, 'product_cat', array( 'fields' => 'slugs' ) );
			$item_total = (float) $item->get_total();

			foreach ( $categories as $category ) {
				if ( ! isset( $category_orders[ $category ] ) ) {
					$category_orders[ $category ] = 0;
				}
				$category_orders[ $category ] += $item_total;
			}
		}

		foreach ( $category_orders as $category => $amount ) {
			// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix, safe.
			$updated = $wpdb->query( $wpdb->prepare(
				"UPDATE {$table}
				 SET order_count = order_count + 1,
				     order_value = order_value + %f,
				     updated_at = %s
				 WHERE email_hash = %s AND category_slug = %s",
				$amount,
				current_time( 'mysql' ),
				$email_hash,
				$category
			) );
			// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter

			if ( 0 === $updated ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Table name from $wpdb->prefix, safe.
				$wpdb->insert(
					$table,
					array(
						'email_hash'    => $email_hash,
						'category_slug' => $category,
						'order_count'   => 1,
						'order_value'   => $amount,
						'refund_count'  => 0,
						'refund_value'  => 0,
						'created_at'    => current_time( 'mysql' ),
						'updated_at'    => current_time( 'mysql' ),
					),
					array( '%s', '%s', '%d', '%f', '%d', '%f', '%s', '%s' )
				);
			}
		}
	}

	/**
	 * Calculate signal score based on category-weighted returns.
	 *
	 * @since 1.0.0
	 * @param string $email_hash Customer email hash.
	 * @return array Signal data.
	 */
	public function get_signal( string $email_hash, ?object $customer = null ): array {
		global $wpdb;

		$table = $wpdb->prefix . 'trustlens_category_stats';

		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix, safe.
		$stats = $wpdb->get_results( $wpdb->prepare(
			"SELECT * FROM {$table} WHERE email_hash = %s",
			$email_hash
		) );
		// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter

		if ( empty( $stats ) ) {
			return array( 'score' => 0, 'reason' => '' );
		}

		$weights = $this->get_category_weights();
		$score = 0;
		$reasons = array();
		$high_risk_refunds = array();

		foreach ( $stats as $stat ) {
			$category = $stat->category_slug;
			$weight = $weights[ $category ] ?? $weights['default'];
			$refund_count = (int) $stat->refund_count;
			$order_count = (int) $stat->order_count;

			if ( 0 === $order_count || 0 === $refund_count ) {
				continue;
			}

			$category_return_rate = ( $refund_count / max( $order_count, 1 ) ) * 100;

			// High-risk category with high return rate.
			if ( $weight >= 1.5 && $category_return_rate >= 30 ) {
				$penalty = (int) ( -15 * $weight );
				$score += $penalty;
				$high_risk_refunds[] = $this->get_category_name( $category );
			} elseif ( $category_return_rate >= 50 ) {
				// Any category with very high return rate.
				$penalty = (int) ( -10 * $weight );
				$score += $penalty;
			}
		}

		if ( ! empty( $high_risk_refunds ) ) {
			$reasons[] = sprintf(
				/* translators: %s: list of category names */
				__( 'High returns in: %s', 'trustlens' ),
				implode( ', ', array_slice( $high_risk_refunds, 0, 3 ) )
			);
		}

		// Cap the penalty.
		$score = max( $score, -40 );

		return array(
			'score'  => $score,
			'reason' => implode( '; ', $reasons ),
		);
	}

	/**
	 * Get category risk weights.
	 *
	 * @since 1.0.0
	 * @return array Category weights.
	 */
	private function get_category_weights(): array {
		$custom_weights = get_option( 'trustlens_category_weights', array() );

		if ( ! empty( $custom_weights ) && is_array( $custom_weights ) ) {
			return array_merge( $this->default_weights, $custom_weights );
		}

		return $this->default_weights;
	}

	/**
	 * Get category display name.
	 *
	 * @since 1.0.0
	 * @param string $slug Category slug.
	 * @return string Category name.
	 */
	private function get_category_name( string $slug ): string {
		$term = get_term_by( 'slug', $slug, 'product_cat' );

		if ( $term && ! is_wp_error( $term ) ) {
			return $term->name;
		}

		return ucfirst( str_replace( '-', ' ', $slug ) );
	}

	/**
	 * Get customer category statistics.
	 *
	 * @since 1.0.0
	 * @param string $email_hash Customer email hash.
	 * @return array Category stats.
	 */
	public function get_customer_category_stats( string $email_hash ): array {
		global $wpdb;

		$table = $wpdb->prefix . 'trustlens_category_stats';

		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix, safe.
		$stats = $wpdb->get_results( $wpdb->prepare(
			"SELECT * FROM {$table} WHERE email_hash = %s ORDER BY refund_count DESC",
			$email_hash
		) );
		// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter

		$result = array();
		foreach ( $stats as $stat ) {
			$result[] = array(
				'category'     => $stat->category_slug,
				'name'         => $this->get_category_name( $stat->category_slug ),
				'order_count'  => (int) $stat->order_count,
				'order_value'  => (float) $stat->order_value,
				'refund_count' => (int) $stat->refund_count,
				'refund_value' => (float) $stat->refund_value,
				'return_rate'  => $stat->order_count > 0
					? round( ( $stat->refund_count / $stat->order_count ) * 100, 1 )
					: 0,
			);
		}

		return $result;
	}

	/**
	 * Get settings fields.
	 *
	 * @since 1.0.0
	 * @return array Settings fields.
	 */
	public function get_settings_fields(): array {
		return array(
			array(
				'id'          => 'category_weights',
				'title'       => __( 'Category Risk Weights', 'trustlens' ),
				'description' => __( 'Configure risk weights for product categories. Higher weight = returns impact score more.', 'trustlens' ),
				'type'        => 'category_weights',
				'default'     => $this->default_weights,
			),
		);
	}
}
