<?php

namespace WPKIN_SHOP_MANAGER\Modules;

if ( ! defined( 'ABSPATH' ) ) {
	wp_die( esc_html__( 'You can\'t access this page', 'shop-manager' ) );
}

/**
 * Customer Auto-Tagging Module
 *
 * Tags are stored in two separate meta keys:
 *   _sm_customer_tags_auto   → system-generated (overwritten each run)
 *   _sm_customer_tags_manual → user-added (NEVER overwritten by system)
 *
 * Strategy:
 *   - Order-triggered tags recalculate instantly via WC hooks
 *   - Time-based tags (at_risk, inactive) use a 24h transient per customer
 *   - Existing customers are lazy-backfilled on first API read (per page)
 */
class CustomerTagger {

	/**
	 * Register hooks.
	 */
	public static function init() {
		// Recalculate tags when an order status changes
		add_action( 'woocommerce_order_status_changed', [ __CLASS__, 'on_order_status_change' ], 10, 3 );

		// Recalculate tags when a new order is placed
		add_action( 'woocommerce_checkout_order_created', [ __CLASS__, 'on_new_order' ] );

		// Tag new customer on registration
		add_action( 'user_register', [ __CLASS__, 'on_user_register' ] );
	}

	// ─────────────────────────────────────────────────────────────────────────
	// Hook Callbacks
	// ─────────────────────────────────────────────────────────────────────────

	public static function on_order_status_change( $order_id, $_old_status, $_new_status ) {
		$order = wc_get_order( $order_id );
		if ( ! $order ) {
			return;
		}
		$customer_id = (int) $order->get_customer_id();
		if ( $customer_id > 0 ) {
			self::recalculate( $customer_id );
		} else {
			// Guest order — find registered user by billing email
			$user = get_user_by( 'email', $order->get_billing_email() );
			if ( $user ) {
				self::recalculate( $user->ID );
			}
		}
	}

	public static function on_new_order( $order ) {
		$customer_id = (int) $order->get_customer_id();
		if ( $customer_id > 0 ) {
			self::recalculate( $customer_id );
		} else {
			// Guest order — find registered user by billing email
			$user = get_user_by( 'email', $order->get_billing_email() );
			if ( $user ) {
				self::recalculate( $user->ID );
			}
		}
	}

	public static function on_user_register( $user_id ) {
		// New user has no orders yet — only 'new' tag could apply later.
		// We'll leave it to be lazy-calculated on first API read.
		delete_user_meta( $user_id, '_sm_customer_tags_auto' );
	}

	// ─────────────────────────────────────────────────────────────────────────
	// Core
	// ─────────────────────────────────────────────────────────────────────────

	/**
	 * Recalculate and persist auto-tags for one customer.
	 * Also clears the time-based transient so it refreshes immediately.
	 */
	public static function recalculate( int $user_id ): array {
		delete_transient( 'sm_customer_time_tags_' . $user_id );
		$tags = self::calculate_tags( $user_id );
		update_user_meta( $user_id, '_sm_customer_tags_auto', $tags );
		return $tags;
	}

	/**
	 * Lazy-read: return stored tags if available, otherwise calculate and store.
	 * This is the backfill path — called per customer on first API read.
	 */
	public static function get_or_calculate( int $user_id ): array {
		$stored = get_user_meta( $user_id, '_sm_customer_tags_auto', true );
		if ( is_array( $stored ) ) {
			// Refresh time-based tags if transient has expired
			return self::maybe_refresh_time_tags( $user_id, $stored );
		}
		return self::recalculate( $user_id );
	}

	/**
	 * Re-evaluate at_risk / inactive without a full recalculate if transient still fresh.
	 */
	private static function maybe_refresh_time_tags( int $user_id, array $tags ): array {
		$cache_key = 'sm_customer_time_tags_' . $user_id;
		if ( false !== get_transient( $cache_key ) ) {
			return $tags; // still fresh
		}

		$settings      = get_option( 'wpkin_sm_admin_settings', [] );
		$tc            = $settings['customer_tagging'] ?? [];
		$at_risk_days  = (int) ( $tc['at_risk_days']  ?? 90  );
		$inactive_days = (int) ( $tc['inactive_days'] ?? 180 );

		// Recalculate time-sensitive tags only
		$metrics    = self::get_metrics( $user_id );
		$time_tags  = self::time_based_tags( $metrics, $at_risk_days, $inactive_days );

		// Merge: remove old time-based tags, add fresh ones
		$non_time = array_diff( $tags, [ 'at_risk', 'inactive' ] );
		$merged   = array_values( array_unique( array_merge( $non_time, $time_tags ) ) );

		update_user_meta( $user_id, '_sm_customer_tags_auto', $merged );
		set_transient( $cache_key, 1, DAY_IN_SECONDS );
		return $merged;
	}

	// ─────────────────────────────────────────────────────────────────────────
	// Tag Calculation
	// ─────────────────────────────────────────────────────────────────────────

	/**
	 * Full tag calculation for a customer.
	 */
	public static function calculate_tags( int $user_id ): array {
		$metrics = self::get_metrics( $user_id );
		$tags    = [];

		$settings = get_option( 'wpkin_sm_admin_settings', [] );
		$tc       = $settings['customer_tagging'] ?? [];

		$vip_spend       = (float) ( $tc['vip_spend_threshold']    ?? 500 );
		$vip_orders      = (int)   ( $tc['vip_orders_threshold']   ?? 10  );
		$loyal_orders    = (int)   ( $tc['loyal_orders_threshold'] ?? 5   );
		$loyal_window    = (int)   ( $tc['loyal_days_window']      ?? 60  );
		$new_days        = (int)   ( $tc['new_days_threshold']     ?? 30  );
		$big_spend       = (float) ( $tc['big_spender_threshold']  ?? 200 );
		$freq_count      = (int)   ( $tc['frequent_orders_count']  ?? 3   );
		$at_risk_days    = (int)   ( $tc['at_risk_days']           ?? 90  );
		$inactive_days   = (int)   ( $tc['inactive_days']          ?? 180 );

		$total_orders    = $metrics['total_orders'];
		$total_spent     = $metrics['total_spent'];
		$last_order_days = $metrics['last_order_days']; // days since last order, -1 = never
		$registered_days = $metrics['registered_days'];
		$max_order_total = $metrics['max_order_total'];
		$recent_orders   = $metrics['recent_orders'];   // orders in last $frequent_window days

		// VIP: high spend OR many orders
		if ( $total_spent > $vip_spend || $total_orders > $vip_orders ) {
			$tags[] = 'vip';
		}

		// Loyal: enough orders AND ordered recently
		if ( $total_orders >= $loyal_orders && $last_order_days >= 0 && $last_order_days < $loyal_window ) {
			$tags[] = 'loyal';
		}

		// New: registered within threshold AND at least 1 order
		if ( $registered_days < $new_days && $total_orders >= 1 ) {
			$tags[] = 'new';
		}

		// Big spender: any single order exceeds threshold
		if ( $max_order_total > $big_spend ) {
			$tags[] = 'big_spender';
		}

		// Frequent: enough orders in recent window
		if ( $recent_orders >= $freq_count ) {
			$tags[] = 'frequent';
		}

		// Time-based (also cached separately)
		$tags = array_merge( $tags, self::time_based_tags( $metrics, $at_risk_days, $inactive_days ) );

		set_transient( 'sm_customer_time_tags_' . $user_id, 1, DAY_IN_SECONDS );

		return array_values( array_unique( $tags ) );
	}

	/**
	 * Only the time-sensitive tags (at_risk, inactive).
	 */
	private static function time_based_tags( array $metrics, int $at_risk_days = 90, int $inactive_days = 180 ): array {
		$tags            = [];
		$total_orders    = $metrics['total_orders'];
		$last_order_days = $metrics['last_order_days'];
		$registered_days = $metrics['registered_days'];

		// At risk: 2+ orders AND no order in last N days (but not yet inactive)
		if ( $total_orders >= 2 && $last_order_days >= 0 && $last_order_days > $at_risk_days && $last_order_days <= $inactive_days ) {
			$tags[] = 'at_risk';
		}

		// Inactive: has orders but last order was over inactive_days ago, OR registered long ago with zero orders
		if ( ( $total_orders >= 1 && $last_order_days > $inactive_days ) ||
		     ( $registered_days > $inactive_days && $total_orders === 0 ) ) {
			$tags[] = 'inactive';
		}

		return $tags;
	}

	/**
	 * Gather all metrics needed for tagging.
	 * Uses 'customer' => $email so guest orders on the same email are included.
	 */
	private static function get_metrics( int $user_id ): array {
		$settings        = get_option( 'wpkin_sm_admin_settings', [] );
		$tc              = $settings['customer_tagging'] ?? [];
		$frequent_window = (int) ( $tc['frequent_orders_window'] ?? 30 );

		$user  = get_userdata( $user_id );
		$email = $user ? $user->user_email : '';

		// Use 'customer' => email — WooCommerce matches both user_id AND billing_email,
		// so guest orders placed with the same email are included automatically.
		$customer_key = $email ?: $user_id;

		// Total orders & spent — query by email to include guest orders
		$all_order_ids = wc_get_orders( [
			'customer' => $customer_key,
			'limit'    => -1,
			'return'   => 'ids',
		] );
		$total_orders = count( $all_order_ids );

		$total_spent = 0.0;
		foreach ( $all_order_ids as $oid ) {
			$o = wc_get_order( $oid );
			if ( $o && in_array( $o->get_status(), [ 'completed', 'processing' ], true ) ) {
				$total_spent += (float) $o->get_total();
			}
		}

		$registered_days = $user
			? (int) floor( ( time() - strtotime( $user->user_registered ) ) / DAY_IN_SECONDS )
			: 0;

		// Last order date (all statuses)
		$last_order_days = -1;
		$last_orders     = wc_get_orders( [
			'customer' => $customer_key,
			'limit'    => 1,
			'orderby'  => 'date',
			'order'    => 'DESC',
		] );
		if ( ! empty( $last_orders ) ) {
			$date = $last_orders[0]->get_date_created();
			if ( $date ) {
				$last_order_days = (int) floor( ( time() - $date->getTimestamp() ) / DAY_IN_SECONDS );
			}
		}

		// Max single order total (completed/processing only)
		$max_order_total = 0.0;
		$paid_orders     = wc_get_orders( [
			'customer' => $customer_key,
			'limit'    => -1,
			'status'   => [ 'wc-completed', 'wc-processing' ],
		] );
		foreach ( $paid_orders as $order ) {
			$t = (float) $order->get_total();
			if ( $t > $max_order_total ) {
				$max_order_total = $t;
			}
		}

		// Orders in last N days
		$window_date   = date( 'Y-m-d', strtotime( "-{$frequent_window} days" ) );
		$recent_orders = count( wc_get_orders( [
			'customer'     => $customer_key,
			'limit'        => -1,
			'date_created' => '>' . $window_date,
			'return'       => 'ids',
		] ) );

		return compact(
			'total_orders',
			'total_spent',
			'registered_days',
			'last_order_days',
			'max_order_total',
			'recent_orders'
		);
	}

	// ─────────────────────────────────────────────────────────────────────────
	// Helpers
	// ─────────────────────────────────────────────────────────────────────────

	/**
	 * Return merged auto + manual tags for display.
	 */
	public static function get_display_tags( int $user_id ): array {
		$auto   = get_user_meta( $user_id, '_sm_customer_tags_auto',   true );
		$manual = get_user_meta( $user_id, '_sm_customer_tags_manual', true );
		$auto   = is_array( $auto )   ? $auto   : [];
		$manual = is_array( $manual ) ? $manual : [];
		return array_values( array_unique( array_merge( $auto, $manual ) ) );
	}

	/**
	 * Count customers (non-staff) that have never been auto-tagged yet.
	 */
	public static function count_untagged(): int {
		// Get the same staff roles excluded in the customer list endpoint
		$staff_roles = apply_filters( 'wpkin_sm_exclude_customer_roles', [
			'administrator', 'editor', 'author', 'contributor',
			'shop_manager', 'wpseo_manager', 'wpseo_editor',
		] );

		// All non-staff users
		$all_users = new \WP_User_Query( [
			'fields'       => 'ID',
			'number'       => -1,
			'role__not_in' => $staff_roles,
			'count_total'  => false,
		] );
		$all_ids = $all_users->get_results();

		if ( empty( $all_ids ) ) {
			return 0;
		}

		// Of those, how many already have auto-tags stored?
		global $wpdb;
		$ids_list = implode( ',', array_map( 'intval', $all_ids ) );
		$tagged   = (int) $wpdb->get_var( $wpdb->prepare(
			"SELECT COUNT(DISTINCT user_id) FROM {$wpdb->usermeta}
			 WHERE meta_key = %s AND user_id IN ({$ids_list})",
			'_sm_customer_tags_auto'
		) );

		return max( 0, count( $all_ids ) - $tagged );
	}
}
