<?php
/**
 * Activity Formatter Service
 *
 * Handles formatting of activity log entries for frontend display.
 *
 * @package Awesomemotive\Rewardswp\Services
 * @since 1.2.0
 */

namespace Awesomemotive\Rewardswp\Services;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use Awesomemotive\Rewardswp\Interfaces\IntegrationInterface;
use function Awesomemotive\Rewardswp\Helpers\format_currency;
use function Awesomemotive\Rewardswp\Helpers\format_name_first_and_initial;
use function Awesomemotive\Rewardswp\Helpers\get_human_readable_date;
use function Awesomemotive\Rewardswp\Helpers\get_member;
use function Awesomemotive\Rewardswp\Helpers\get_referral;
use function Awesomemotive\Rewardswp\Helpers\get_reward;
use function Awesomemotive\Rewardswp\Helpers\get_reward_display_title_from_values;
use function Awesomemotive\Rewardswp\Helpers\mask_email;

/**
 * Activity Formatter Service.
 *
 * @since 1.2.0
 */
class ActivityFormatterService {
	/**
	 * Integration instance.
	 *
	 * @since 1.2.0
	 * @var IntegrationInterface
	 */
	private IntegrationInterface $integration;

	/**
	 * Referrals service instance.
	 *
	 * @since 1.2.0
	 * @var ReferralsService
	 */
	private ReferralsService $referrals_service;

	/**
	 * Constructor.
	 *
	 * @since 1.2.0
	 *
	 * @param IntegrationInterface $integration       Integration instance.
	 * @param ReferralsService     $referrals_service Referrals service instance.
	 */
	public function __construct(
		IntegrationInterface $integration,
		ReferralsService $referrals_service
	) {
		$this->integration       = $integration;
		$this->referrals_service = $referrals_service;
	}

	/**
	 * Format points activity entries for frontend display.
	 *
	 * @since 1.2.0
	 *
	 * @param array $raw_items Raw points entries from repository.
	 *
	 * @return array Formatted activity items.
	 */
	public function format_points_activity( array $raw_items ): array {
		$formatted = [];

		foreach ( $raw_items as $item ) {
			$source_type  = $item['source_type'] ?? '';
			$source_id    = $item['source_id'] ?? null;
			$points       = (int) ( $item['points'] ?? 0 );
			$type         = $item['type'] ?? 'earned';
			// 'expired' type is no longer used but kept for backward compatibility with older entries.
			$is_deduction = in_array( $type, [ 'spent', 'expired' ], true ) || 'points_expiration' === $source_type;

			// Map source_type to display labels.
			$action_label = '';
			$target_label = '';

			switch ( $source_type ) {
				case 'purchase':
					$action_label = __( 'Earned', 'rewardswp' );
					$target_label = __( 'Made a purchase', 'rewardswp' );
					break;

				case 'first_order':
					$action_label = __( 'Earned', 'rewardswp' );
					$target_label = __( 'First order bonus', 'rewardswp' );
					break;

				case 'reward_redemption':
					$action_label = __( 'Spent points', 'rewardswp' );
					$target_label = $this->get_reward_redemption_label( $source_id );
					break;

				case 'manual_adjustment':
					$action_label = __( 'Adjusted', 'rewardswp' );
					$target_label = ! empty( $item['note_public'] )
						? $item['note_public']
						: __( 'Adjusted by store', 'rewardswp' );
					break;

				case 'referral':
				case 'referral_reward':
					$action_label = __( 'Earned', 'rewardswp' );
					$target_label = __( 'Referred a friend', 'rewardswp' );
					break;

				case 'refund':
					$action_label = __( 'Refunded', 'rewardswp' );
					$target_label = __( 'Order refunded', 'rewardswp' );
					break;

				case 'partial_refund':
					$action_label = __( 'Refunded', 'rewardswp' );
					$target_label = __( 'Partial refund', 'rewardswp' );
					break;

				case 'product_review':
				case 'edd_product_review':
					$action_label = __( 'Earned', 'rewardswp' );
					$target_label = __( 'Wrote a product review', 'rewardswp' );
					break;

				case 'signup':
					$action_label = __( 'Earned', 'rewardswp' );
					$target_label = __( 'Created an account', 'rewardswp' );
					break;

				case 'birthday':
					$action_label = __( 'Earned', 'rewardswp' );
					$target_label = __( 'Birthday bonus', 'rewardswp' );
					break;

				case 'points_expiration':
					$action_label = __( 'Expired', 'rewardswp' );
					$target_label = __( 'Points expired', 'rewardswp' );
					break;

				default:
					$action_label = $is_deduction
						? __( 'Spent', 'rewardswp' )
						: __( 'Earned', 'rewardswp' );
					if ( ! empty( $item['note_public'] ) ) {
						$target_label = $item['note_public'];
					} else {
						// Fallback: use action_label as target_label if no note_public
						$target_label = $action_label;
					}
			}

			// Ensure target_label always has a value (fallback to action_label)
			if ( empty( $target_label ) ) {
				$target_label = $action_label;
			}

			$formatted_item = [
				'id'           => $item['id'],
				'action'       => $action_label,
				'target'       => $target_label,
				'points'       => $is_deduction ? -$points : $points,
				'is_deduction' => $is_deduction,
				'source_type'  => $source_type,
				'date'         => get_human_readable_date( $item['created_at'], true, false ),
				'date_raw'     => $item['created_at'],
				'status'       => $item['status'] ?? null, // Include status for status pills
			];

			$formatted[] = $formatted_item;
		}

		return $formatted;
	}

	/**
	 * Format referrals activity entries for frontend display.
	 *
	 * @since 1.2.0
	 *
	 * @param array $raw_items Raw referral entries from repository.
	 *
	 * @return array Formatted activity items.
	 */
	public function format_referrals_activity( array $raw_items ): array {
		$formatted = [];

		foreach ( $raw_items as $item ) {
			$status = $item['status'] ?? '';

			$friend_name = $this->get_friend_name_from_referral( (int) $item['id'] );
			$order_id      = ! empty( $item['order_id'] ) ? (int) $item['order_id'] : null;
			$status_labels = $this->get_referral_status_labels( $status, $order_id );

			// Keep target_label for backward compatibility, but also provide separate fields
			$target_label = ! empty( $status_labels['action_text'] )
				? sprintf( '%s %s', $friend_name, $status_labels['action_text'] )
				: $friend_name;

			$formatted[] = [
				'id'          => $item['id'],
				'action'      => $status_labels['action_label'],
				'target'      => $target_label,
				'friend_name' => $friend_name,
				'action_text' => $status_labels['action_text'],
				'status'      => $status,
				'date'        => get_human_readable_date( $item['created_at'], true, false ),
				'date_raw'    => $item['created_at'],
			];
		}

		return $formatted;
	}

	/**
	 * Get reward redemption label for points activity.
	 *
	 * @since 1.2.0
	 *
	 * @param int|null $source_id Reward ID.
	 *
	 * @return string Formatted reward label.
	 */
	private function get_reward_redemption_label( ?int $source_id ): string {
		if ( empty( $source_id ) ) {
			return '';
		}

		$reward = get_reward( $source_id );
		if ( ! $reward ) {
			return '';
		}

		// For free_product rewards, use same logic as prepare_reward_for_alpine()
		// Priority: reward_description (custom name) > free_product_name > products_included name
		if ( 'free_product' === $reward->reward_type ) {
			$reward_description = $reward->get_meta( 'reward_description' ) ?? '';
			$product_name      = $reward->get_meta( 'free_product_name' ) ?? '';
			$products_included = $reward->get_meta( 'products_included' );

			// Use custom description if set, otherwise fall back to product name
			$display_name = ! empty( $reward_description ) ? $reward_description : $product_name;

			// If still empty, try products_included
			if ( empty( $display_name ) && ! empty( $products_included ) && is_array( $products_included ) && ! empty( $products_included[0]['name'] ) ) {
				$display_name = sanitize_text_field( $products_included[0]['name'] );
			}

			if ( ! empty( $display_name ) ) {
				$product_value = $reward->get_meta( 'product_value' );
				if ( ! empty( $product_value ) && floatval( $product_value ) > 0 ) {
					return sprintf(
						/* translators: %1$s: formatted currency amount (e.g., "$500"), %2$s: product name */
						__( '%1$s off %2$s', 'rewardswp' ),
						format_currency( floatval( $product_value ) ),
						$display_name
					);
				} else {
					return sprintf(
						/* translators: %s: product name */
						__( 'Redeemed: %s', 'rewardswp' ),
						$display_name
					);
				}
			}
		}

		// Fallback to default display title
		return get_reward_display_title_from_values(
			null,
			$reward->reward_type,
			(float) $reward->amount
		);
	}

	/**
	 * Get friend name from referral.
	 *
	 * @since 1.2.0
	 *
	 * @param int $referral_id Referral ID.
	 *
	 * @return string Formatted friend name.
	 */
	private function get_friend_name_from_referral( int $referral_id ): string {
		$referral = get_referral( $referral_id );
		if ( ! $referral ) {
			return __( 'A friend', 'rewardswp' );
		}

		// Get customer name using the same logic as ReferralsListTableView.
		$customer_name = $this->referrals_service->get_referred_friend_name( $referral );

		if ( 'N/A' === $customer_name ) {
			return __( 'A friend', 'rewardswp' );
		}

		// Try to get member by user_id (same logic as ReferralsListTableView).
		$referred_member = null;
		if ( ! empty( $referral->referred_user_id ) ) {
			$referred_member = get_member( $referral->referred_user_id );
		}

		// If member found, use member's full name formatted as first name + initial.
		if ( $referred_member && $referred_member->id ) {
			$full_name = $referred_member->get_full_name();
			return format_name_first_and_initial( $full_name );
		}

		// If not a member, check if customer_name is an email and mask it.
		if ( filter_var( $customer_name, FILTER_VALIDATE_EMAIL ) ) {
			return mask_email( $customer_name );
		}

		// Format customer name as first name + initial (already a name from order or user).
		return format_name_first_and_initial( $customer_name );
	}

	/**
	 * Get referral status labels.
	 *
	 * @since 1.2.0
	 *
	 * @param string   $status   Referral status.
	 * @param int|null $order_id Order ID, if referral is order-based.
	 *
	 * @return array Array with 'action_label' and 'action_text' keys.
	 */
	private function get_referral_status_labels( string $status, ?int $order_id = null ): array {
		switch ( $status ) {
			case 'completed':
				if ( empty( $order_id ) ) {
					return [
						'action_label' => __( 'Reward claimed', 'rewardswp' ),
						'action_text'  => __( 'claimed their reward', 'rewardswp' ),
					];
				}
				return [
					'action_label' => __( 'Referral completed', 'rewardswp' ),
					'action_text'  => __( 'made a purchase', 'rewardswp' ),
				];

			case 'pending':
				if ( empty( $order_id ) ) {
					return [
						'action_label' => __( 'Reward claimed', 'rewardswp' ),
						'action_text'  => __( 'claimed their reward', 'rewardswp' ),
					];
				}
				return [
					'action_label' => __( 'Order pending', 'rewardswp' ),
					'action_text'  => __( 'placed an order', 'rewardswp' ),
				];

			case 'blocked':
				return [
					'action_label' => __( 'Referral blocked', 'rewardswp' ),
					'action_text'  => __( 'referral was blocked', 'rewardswp' ),
				];

			case 'canceled':
				return [
					'action_label' => __( 'Referral canceled', 'rewardswp' ),
					'action_text'  => __( 'referral was canceled', 'rewardswp' ),
				];

			case 'rejected':
				return [
					'action_label' => __( 'Referral rejected', 'rewardswp' ),
					'action_text'  => __( 'referral was rejected', 'rewardswp' ),
				];

			default:
				return [
					'action_label' => __( 'Referral', 'rewardswp' ),
					'action_text'  => '',
				];
		}
	}

	/**
	 * Format rewards activity entries for frontend display.
	 *
	 * @since 1.2.0
	 *
	 * @param array $raw_items Raw reward entries from repository.
	 *
	 * @return array Formatted activity items.
	 */
	public function format_rewards_activity( array $raw_items ): array {
		$formatted = [];

		foreach ( $raw_items as $item ) {
			$status      = $item['status'] ?? '';
			$reward_type = $item['reward_type'] ?? '';
			$amount      = (float) ( $item['amount'] ?? 0 );

			// Get reward display title.
			$reward_title = get_reward_display_title_from_values( null, $reward_type, $amount );

			// Map status to display labels.
			switch ( $status ) {
				case 'issued':
					$action_label = __( 'Reward issued', 'rewardswp' );
					break;

				case 'redeemed':
					$action_label = __( 'Reward used', 'rewardswp' );
					break;

				case 'pending':
					$action_label = __( 'Reward pending', 'rewardswp' );
					break;

				case 'canceled':
					$action_label = __( 'Reward canceled', 'rewardswp' );
					break;

				default:
					$action_label = __( 'Reward', 'rewardswp' );
			}

			$date_to_use = ! empty( $item['applied_at'] ) && 'redeemed' === $status
				? $item['applied_at']
				: $item['created_at'];

			$formatted[] = [
				'id'       => $item['id'],
				'action'   => $action_label,
				'target'   => $reward_title,
				'status'   => $status,
				'date'     => get_human_readable_date( $date_to_use, true, false ),
				'date_raw' => $date_to_use,
			];
		}

		return $formatted;
	}
}
