<?php
/**
 * Referrals service for managing referral lifecycle and queries.
 *
 * @package RewardsWP
 */

namespace Awesomemotive\Rewardswp\Services;

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

use Awesomemotive\Rewardswp\Abstracts\AbstractModelService;
use Awesomemotive\Rewardswp\Database\DatabaseHandler;
use Awesomemotive\Rewardswp\Database\Tables\EventsTable;
use Awesomemotive\Rewardswp\Database\Tables\ReferralsTable;
use Awesomemotive\Rewardswp\Interfaces\IntegrationWithCouponInterface;
use Awesomemotive\Rewardswp\Models\Referral;
use DI\Container;
use function Awesomemotive\Rewardswp\Helpers\get_current_time;
use Awesomemotive\Rewardswp\Interfaces\IntegrationInterface;
use Awesomemotive\Rewardswp\Factories\IntegrationsFactory;
use function Awesomemotive\Rewardswp\Helpers\get_user_full_name;
use function Awesomemotive\Rewardswp\Helpers\kses;

class ReferralsService extends AbstractModelService {

	/**
	 * Events database table instance.
	 *
	 * @since 1.0.0
	 *
	 * @var EventsTable
	 */
	private EventsTable $events_db_table;

	/**
	 * Integrations factory instance.
	 *
	 * @since 1.0.0
	 *
	 * @var IntegrationsFactory
	 */
	private IntegrationsFactory $integration_factory;

	public function __construct(
		DatabaseHandler $db_handler,
		ReferralsTable $db_table,
		EventsTable $events_db_table,
		Container $container,
		IntegrationsFactory $integration_factory
	) {
		parent::__construct( $db_handler, $db_table, $container );

		$this->events_db_table     = $events_db_table;
		$this->integration_factory = $integration_factory;
	}

	/**
	 * Retrieve referrals based on the given parameters and pagination options.
	 *
	 * @since 1.0.0
	 *
	 * @param array $where    An associative array of conditions to filter referrals.
	 *                        See {@see ReferralsTable::get_columns()} for available parameters.
	 * @param int   $per_page Optional. The number of items per page for pagination. Defaults to 10.
	 * @param int   $page     Optional. The page number for pagination. Defaults to 1.
	 *
	 * @return Referral[] Array of Referrals objects.
	 */
	public function get_referrals( array $where = [], int $per_page = 10, int $page = 1 ): array {
		return $this->get_objects( Referral::class, $where, $per_page, $page );
	}

	/**
	 * Get the number of referrals based on the given parameters.
	 *
	 * @since 1.0.0
	 *
	 * @param array $where An associative array of conditions to filter referrals.
	 *                     See {@see ReferralsTable::get_columns()} for available parameters.
	 * @return int The number of entries found.
	 */
	public function count_referrals( array $where = [] ): int {
		return $this->count_objects( $where );
	}

	/**
	 * Retrieve a list of referrals from a given IP address.
	 *
	 * @since 1.0.0
	 *
	 * @param string $ip_address
	 * @param int    $per_page
	 * @param int    $page
	 *
	 * @return array
	 */
	public function get_referrals_from_ip( string $ip_address, int $per_page = 10, int $page = 1 ): array {
		return $this->get_objects(
			Referral::class,
			[
				'join_query' => [
					[
						'type'  => 'INNER',
						'table' => $this->events_db_table->get_prefixed_table_name(),
						'on'    => sprintf(
							'%s.event_id = %s.id',
							$this->db_table->get_prefixed_table_name(),
							$this->events_db_table->get_prefixed_table_name()
						),
						'where' => [
							'ip_address' => $ip_address,
						],
					],
				],
				'date_query' => [
					'column' => sprintf( '%s.created_at', $this->db_table->get_prefixed_table_name() ),
					[
						'after'     => get_current_time( 'Y-m-d', strtotime( '-7 days' ) ),
						'inclusive' => true,
						'compare'   => '>=',
					],
				],
			],
			$per_page,
			$page,
			implode(
				', ',
				array_map(
					fn( $column ) => $this->db_table->get_prefixed_table_name() . '.' . $column,
					array_keys( $this->db_table->get_columns() )
				)
			)
		);
	}

	/**
	 * Get the name of a referred friend.
	 *
	 * @since 1.0.0
	 *
	 * @param Referral $referral The referral object.
	 *
	 * @return string The name of the referred friend.
	 */
	public function get_referred_friend_name( Referral $referral ): string {
		$integration   = $this->integration_factory->get_integration( $referral->source );
		$not_available = esc_html__( 'N/A', 'rewardswp' );

		if ( ! is_subclass_of( $integration, IntegrationInterface::class ) ) {
			return $not_available;
		}

		// Try to retrieve the customer name from the order first
		$customer_name = $integration->get_customer_name( $referral->order_id ?? 0 );

		// If fails to retrieve from the order, try by the WP User
		if ( empty( $customer_name ) ) {
			$user          = get_user( $referral->referred_user_id ?? 0 );
			$customer_name = $user
				? get_user_full_name( $referral->referred_user_id )
				: $not_available;
		}

		// If still no name, try to use the email
		if ( $not_available === $customer_name && ! empty( $referral->referred_email ) ) {
			$customer_name = $referral->referred_email;
		}

		return $customer_name;
	}

	/**
	 * Get the referral order link for a given referral.
	 *
	 * @since 1.0.0
	 *
	 * @param Referral $referral The referral object containing the source and associated data.
	 * @return string|null The URL of the referred order if available, otherwise null.
	 */
	public function get_referred_order_link( Referral $referral ): ?string {
		$integration = $this->integration_factory->get_integration( $referral->source );

		if ( ! is_subclass_of( $integration, IntegrationWithCouponInterface::class ) || empty( $referral->order_id ) ) {
			return null;
		}

		return esc_url_raw( $integration->get_order_link( $referral->order_id ) );
	}

	/**
	 * Delete all referrals and their associated metadata for a given member ID (advocate_id).
	 *
	 * @since 1.0.0
	 *
	 * @param int $member_id The ID of the member (advocate).
	 * @return bool True if all deletions were successful or if no referrals/metadata existed, false on any error.
	 */
	public function delete_by_member_id( int $member_id ): bool {
		if ( $member_id <= 0 ) {
			return false;
		}

		// Get all referral IDs associated with this member_id (advocate_id)
		$referral_ids = $this->db_handler->get_results(
			$this->db_table->get_prefixed_table_name(),
			[ 'advocate_id' => $member_id ],
			'id',
			apply_filters( 'am_rewardswp_unlimited', 99999 )
		);

		$all_successful = true;

		if ( ! empty( $referral_ids ) ) {
			$meta_table_name         = $this->db_table->get_prefixed_meta_table_name();
			$meta_relationship_field = $this->db_table->get_meta_table_relationship_field_name();

			foreach ( $referral_ids as $referral_row ) {
				$referral_id = $referral_row['id'] ?? null;
				if ( $referral_id && $meta_table_name && $meta_relationship_field ) {
					$meta_deleted = $this->db_handler->delete(
						$meta_table_name,
						[ $meta_relationship_field => $referral_id ]
					);
					if ( false === $meta_deleted ) {
						$all_successful = false;
						// Optionally log error for this specific referral_id's metadata deletion
					}
				}
			}
		}

		// Now delete the main referral entries
		$referrals_deleted = $this->db_handler->delete(
			$this->db_table->get_prefixed_table_name(),
			[ 'advocate_id' => $member_id ]
		);

		if ( false === $referrals_deleted ) {
			$all_successful = false;
		}

		return $all_successful;
	}
}
