<?php

namespace WC_Affiliate\Controllers\Admin;

defined( 'ABSPATH' ) || exit;

use WC_Affiliate\Models\User;
use WC_Order;
use WC_Order_Item_Product;
use WC_Product;
use WC_Subscription;
use WP_Post;
use WP_User;

/**
 * Admin Controller for WC Affiliate Plugin.
 *
 * Handles all admin-specific functionality including menu setup, dashboard widgets,
 * admin notices, and user profile management for affiliates.
 *
 * @since 1.0.0
 */
class Init {

	/**
	 * Plugin metadata and configuration.
	 *
	 * Contains plugin information including version, file paths, server URLs,
	 * and minimum requirements for PHP and WordPress versions.
	 *
	 * @since 1.0.0
	 * @var array
	 */
	public array $plugin;

	/**
	 * Controller constructor
	 *
	 * Automatically calls the init method if it exists
	 */
	public function __construct() {
		$this->register_hooks();
	}

	/**
	 * Register hooks for admin controller.
	 */
	protected function register_hooks() {
		add_filter( 'plugin_action_links_' . WC_AFFILIATE_BASENAME, array( $this, 'plugin_action_links' ) );
		add_filter( 'plugin_row_meta', array( $this, 'plugin_row_meta' ), 10, 2 );
		add_action( 'admin_init', array( $this, 'maybe_redirect_to_wizard' ) );

		add_action( 'admin_init', array( $this, 'admin_init' ) );
		add_action( 'admin_notices', array( $this, 'admin_notices' ) );
		add_action( 'wc_affiliate_daily', array( $this, 'daily_tasks' ) );
		add_action( 'wp_dashboard_setup', array( $this, 'dashboard_widgets' ) );

		add_filter( 'wp_kses_allowed_html', array( $this, 'allow_extra_html_tags' ) );
		add_filter( 'display_post_states', array( $this, 'add_dashboard_post_state' ), 10, 2 );

		// User profile fields.
		add_action( 'show_user_profile', array( $this, 'show_commission_fields' ) );
		add_action( 'edit_user_profile', array( $this, 'show_commission_fields' ) );
		add_action( 'personal_options_update', array( $this, 'save_commission_fields' ) );
		add_action( 'edit_user_profile_update', array( $this, 'save_commission_fields' ) );

		// Product commission fields.
		add_action( 'woocommerce_product_options_general_product_data', array( $this, 'show_product_commission_fields' ) );
		add_action( 'woocommerce_process_product_meta', array( $this, 'save_product_commission_fields' ) );
		add_action( 'woocommerce_product_after_variable_attributes', array( $this, 'show_variation_commission_fields' ), 10, 3 );
		add_action( 'woocommerce_save_product_variation', array( $this, 'save_variation_commission_fields' ), 10, 2 );

		// Order status changes.
		add_action( 'woocommerce_order_status_changed', array( $this, 'update_referral_status' ), 10, 4 );

		// WooCommerce Subscriptions recurring payments.
		add_action( 'woocommerce_subscription_renewal_payment_complete', array( $this, 'handle_subscription_renewal' ), 10, 2 );
	}

	/**
	 * Add custom action links to the plugin listing page.
	 *
	 * @param array $links Existing plugin action links.
	 *
	 * @return array Modified plugin action links.
	 */
	public function plugin_action_links( array $links ): array {
		$new_links = array();

		$new_links['settings'] = sprintf(
			'<a href="%s">%s</a>',
			admin_url( 'admin.php?page=wc-affiliate' ),
			__( 'Settings', 'wc-affiliate' )
		);

		$new_links['setup-wizard'] = sprintf(
			'<a href="%s">%s</a>',
			admin_url( 'admin.php?page=wc-affiliate#/setup-wizard' ),
			__( 'Setup Wizard', 'wc-affiliate' )
		);

		if ( ! wca_has_pro() ) {
			$new_links['documentation'] = sprintf(
				'<a href="%s" target="_blank">%s</a>',
				'https://wcaffiliate.com/docs/',
				__( 'Documentation', 'wc-affiliate' )
			);
		}

		return apply_filters( 'wc_affiliate_plugin_action_links', array_merge( $new_links, $links ) );
	}

	/**
	 * Add custom meta links to the plugin listing page.
	 *
	 * @param array  $plugin_meta Existing plugin meta links.
	 * @param string $plugin_file Plugin file path.
	 *
	 * @return array Modified plugin meta links.
	 */
	public function plugin_row_meta( array $plugin_meta, string $plugin_file ): array {
		if ( WC_AFFILIATE_BASENAME === $plugin_file ) {
			$plugin_meta['help'] = sprintf(
				'<a href="%s" target="_blank">%s</a>',
				'https://help.codexpert.io/',
				__( 'Help', 'wc-affiliate' )
			);
		}

		return apply_filters( 'wc_affiliate_plugin_row_meta', $plugin_meta, $plugin_file );
	}

	/**
	 * Maybe redirect to setup wizard on first activation.
	 *
	 * Handles automatic redirection to the setup wizard when the plugin
	 * is first activated. Guards against bulk activation, non-admin users,
	 * completed/skipped wizard states, and redirect loops.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function maybe_redirect_to_wizard(): void {
		/**
		 * Action hook fired before wizard redirect check.
		 */
		do_action( 'wc_affiliate_maybe_redirect_to_wizard_before' );

		// Bail early if the activation transient is not set.
		if ( ! get_transient( 'wc_affiliate_activation_redirect' ) ) {
			return;
		}

		// Do not redirect during bulk plugin activation — keep the transient alive.
		if ( isset( $_GET['activate-multi'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			return;
		}

		// Only redirect administrators. Non-admin users leave the transient for the next admin page load.
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}

		// Consume the transient now that all pre-conditions pass.
		delete_transient( 'wc_affiliate_activation_redirect' );

		// Do not redirect if the wizard has already been completed or skipped.
		if ( ! wca_should_show_setup_wizard() ) {
			do_action( 'wc_affiliate_maybe_redirect_to_wizard_after' );
			return;
		}

		// Avoid redirect loops when already on the WC Affiliate admin page.
		if ( isset( $_GET['page'] ) && 'wc-affiliate' === $_GET['page'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			do_action( 'wc_affiliate_maybe_redirect_to_wizard_after' );
			return;
		}

		/**
		 * Action hook fired immediately before the wizard redirect.
		 */
		do_action( 'wc_affiliate_wizard_redirect_before' );

		wp_safe_redirect( admin_url( 'admin.php?page=wc-affiliate#/setup-wizard' ) );
		exit;
	}

	/**
	 * Initialize admin functionality.
	 */
	public function admin_init() {
		// Load text domain.
		load_plugin_textdomain( 'wc-affiliate', false, WC_AFFILIATE_PLUGIN_DIR . 'languages/' );

		/**
		 * Action hook fired after admin initialization.
		 */
		do_action( 'wc_affiliate_admin_init_after' );
	}

	/**
	 * Setup dashboard widgets.
	 */
	public function dashboard_widgets() {
		/**
		 * Action hook fired before dashboard widgets setup.
		 */
		do_action( 'wc_affiliate_dashboard_widgets_before' );

		wp_add_dashboard_widget(
			'wc-affiliate-overview',
			__( 'Latest From Our Blog', 'wc-affiliate' ),
			array( $this, 'dashboard_widget_callback' )
		);

		// Move widget to top.
		global $wp_meta_boxes;
		if ( isset( $wp_meta_boxes['dashboard']['normal']['core']['wc-affiliate-overview'] ) ) {
			$widget = $wp_meta_boxes['dashboard']['normal']['core']['wc-affiliate-overview'];
			unset( $wp_meta_boxes['dashboard']['normal']['core']['wc-affiliate-overview'] );
			$wp_meta_boxes['dashboard']['normal']['core'] = array_merge( // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
				array( 'wc-affiliate-overview' => $widget ),
				$wp_meta_boxes['dashboard']['normal']['core']
			);
		}

		/**
		 * Action hook fired after dashboard widgets setup.
		 */
		do_action( 'wc_affiliate_dashboard_widgets_after' );
	}

	/**
	 * Render dashboard widget content for affiliate statistics.
	 */
	public function dashboard_widget_callback() {
		$posts = get_option( 'wc-affiliate-docs-json', array() );
		$utm   = array(
			'utm_source'   => 'dashboard',
			'utm_medium'   => 'metabox',
			'utm_campaign' => 'blog-post',
		);

		if ( count( $posts ) > 0 ) {
			$posts = array_slice( $posts, 0, 5 );
			echo '<ul id="wc-affiliate-posts-wrapper">';

			foreach ( $posts as $post ) {
				$post_link = add_query_arg( $utm, $post['link'] );
				$title     = sanitize_text_field( $post['title']['rendered'] );
				$content   = wpautop( wp_trim_words( sanitize_text_field( $post['content']['rendered'] ), 10 ) );

				printf(
					'<li><a href="%s" target="_blank"><span class="dashicons dashicons-external"></span> %s</a>%s</li>',
					esc_url( $post_link ),
					esc_html( $title ),
					wp_kses_post( $content )
				);
			}

			echo '</ul>';
		}

		// Footer links.
		$links = apply_filters(
			'wc-affiliate-overview_links',
			array(
				'products' => array(
					'url'    => add_query_arg( $utm, 'https://codexpert.io/products/' ),
					'label'  => __( 'Products', 'wc-affiliate' ),
					'target' => '_blank',
				),
				'hire'     => array(
					'url'    => add_query_arg( $utm, 'https://codexpert.io/hire/' ),
					'label'  => __( 'Hire Us', 'wc-affiliate' ),
					'target' => '_blank',
				),
			)
		);

		$footer_links = array();
		foreach ( $links as $link ) {
			$icon           = ( '_blank' === $link['target'] ) ? '<span class="dashicons dashicons-external"></span>' : '';
			$footer_links[] = sprintf(
				'<a href="%s" target="%s">%s%s</a>',
				esc_url( $link['url'] ),
				esc_attr( $link['target'] ),
				esc_html( $link['label'] ),
				$icon
			);
		}

		if ( ! empty( $footer_links ) ) {
			echo '<p class="community-events-footer">' . implode( ' | ', $footer_links ) . '</p>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		}
	}

	/**
	 * Display admin notices for affiliate-related messages.
	 */
	public function admin_notices() {
		/**
		 * Action hook fired before admin notices display.
		 */
		do_action( 'wc_affiliate_admin_notices_before' );

		if ( get_transient( 'wc_affiliate_activation_redirect' ) ) {
			return;
		}

		// Check if required platform is installed and activated.
		$platform = wca_get_platform();

		if ( 'easycommerce' === $platform ) {
			$required_plugin = 'easycommerce/easycommerce.php';
			$platform_name   = 'Easycommerce';
		} else {
			$required_plugin = 'woocommerce/woocommerce.php';
			$platform_name   = 'WooCommerce';
		}

		if ( ! is_plugin_active( $required_plugin ) ) {
			$class   = 'notice notice-error wc-affiliate-platform-notice';
			$message = sprintf(
			/* translators: 1: Platform name 2: Platform name */
				__( 'The selected platform %1$s is not installed or activated. Please install and activate %2$s to use WC Affiliate.', 'wc-affiliate' ),
				$platform_name,
				$platform_name
			);

			$all_plugins = get_plugins();
			$installed   = isset( $all_plugins[ $required_plugin ] );

			if ( $installed ) {
				// Plugin is installed but not activated.
				$action_text = sprintf(
				/* translators: %s: Plugin name */
					__( 'Activate %s', 'wc-affiliate' ),
					$platform_name
				);
			} else {
				$action_text = sprintf(
				/* translators: %s: Plugin name */
					__( 'Install and Activate %s', 'wc-affiliate' ),
					$platform_name
				);
			}

			printf(
				'<div class="%1$s" style="display: flex; justify-content: space-around; align-items: center; width: auto; padding: 4px 5px;"><p style="width: 100%%;margin:0;">%2$s</p> <a href="#" style="min-width: 230px; text-align: center;" class="resolve-dependency button button-primary">%3$s</a></div>',
				esc_attr( $class ),
				esc_html( $message ),
				esc_html( $action_text )
			);
		}

		/**
		 * Action hook fired after admin notices display.
		 */
		do_action( 'wc_affiliate_admin_notices_after' );
	}

	/**
	 * Synchronizes documentation from the help site daily.
	 *
	 * Fetches documentation data from a specified remote API endpoint
	 * and updates the associated WordPress option if the data retrieval
	 * is successful and free of errors.
	 *
	 * @return void
	 */
	public function daily_tasks() {
		// Sync docs from help site daily.
		if ( defined( 'WC_AFFILIATE_DOC_ID' ) ) {
			$docs_data = wp_remote_get( 'https://help.codexpert.io/wp-json/wp/v2/docs/?parent=' . WC_AFFILIATE_DOC_ID . '&per_page=20' );
			if ( ! is_wp_error( $docs_data ) ) {
				update_option( 'wc-affiliate-docs-json', json_decode( $docs_data['body'], true ) );
			}
		}
	}

	/**
	 * Allow additional HTML tags in post content for affiliate shortcodes.
	 *
	 * @param array $tags Existing allowed HTML tags.
	 *
	 * @return array Modified allowed HTML tags.
	 */
	public function allow_extra_html_tags( array $tags ): array {
		$additional_tags = array(
			'option' => array(
				'value'    => true,
				'selected' => true,
			),
			'script' => array(
				'src'  => true,
				'type' => true,
			),
			'form'   => array(
				'action' => true,
				'method' => true,
				'class'  => true,
				'id'     => true,
			),
			'input'  => array(
				'id'          => true,
				'class'       => true,
				'type'        => true,
				'name'        => true,
				'value'       => true,
				'required'    => true,
				'placeholder' => true,
			),
			'button' => array(
				'type'          => true,
				'class'         => true,
				'id'            => true,
				'data-params'   => true,
				'data-headings' => true,
				'data-name'     => true,
				'disabled'      => true,
				'data-action'   => true,
				'data-id'       => true,
			),
			'select' => array(
				'name'     => true,
				'id'       => true,
				'class'    => true,
				'multiple' => true,
			),
			'table'  => array(
				'class' => true,
				'id'    => true,
			),
			'style'  => array(
				'type' => true,
			),
		);

		return apply_filters( 'wc_affiliate_allowed_html_tags', array_merge_recursive( $tags, $additional_tags ) );
	}

	/**
	 * Show commission fields on user profile page.
	 *
	 * @param WP_User $user User object.
	 */
	public function show_commission_fields( WP_User $user ) {
		echo wp_kses_post( wca_get_template( 'commission-fields', 'views/admin/user', array( 'user' => $user ) ) );
	}

	/**
	 * Save commission fields from user profile page.
	 *
	 * @param int $user_id User ID.
	 */
	public function save_commission_fields( int $user_id ) {
		/**
		 * Action hook fired before saving commission fields.
		 *
		 * @param int $user_id User ID.
		 */
		do_action( 'wc_affiliate_save_commission_fields_before', $user_id );

		// Verify nonce for security.
		if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'update-user_' . $user_id ) ) {
			return;
		}

		$user_model = new User( $user_id );

		if ( isset( $_POST['_wc_affiliate_status'] ) ) {
			$status = sanitize_text_field( wp_unslash( $_POST['_wc_affiliate_status'] ) );
			if ( 'active' === $status ) {
				$user_model->update_meta( '_wc_affiliate_status', $status );
				$user_model->update_meta( '_wc_affiliate_time_applied', time() );
			} else {
				$user_model->update_meta( '_wc_affiliate_status', $status );
			}
		}

		if ( isset( $_POST['commission_type'] ) ) {
			$user_model->update_meta( 'commission_type', sanitize_text_field( wp_unslash( $_POST['commission_type'] ) ) );
		}

		if ( isset( $_POST['commission_amount'] ) ) {
			$user_model->update_meta( 'commission_amount', sanitize_text_field( wp_unslash( $_POST['commission_amount'] ) ) );
		}

		/**
		 * Action hook fired after saving commission fields.
		 *
		 * @param int $user_id User ID.
		 */
		do_action( 'wc_affiliate_save_commission_fields_after', $user_id );
	}

	/**
	 * Display commission fields in WooCommerce product edit page.
	 */
	public function show_product_commission_fields() {
		$product = wc_get_product( get_the_ID() );

		if ( wca_get_commission_base() !== 'product_price' ) {
			return;
		}

		if ( ! $product->is_type( 'simple' ) ) {
			return;
		}

		echo wca_get_template( 'commission-fields', 'views/admin/product', array( 'product' => $product ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	}

	/**
	 * Save commission fields for WooCommerce products.
	 *
	 * @param int $post_id Product post ID.
	 */
	public function save_product_commission_fields( int $post_id ) {
		/**
		 * Action hook fired before saving product commission fields.
		 *
		 * @param int $post_id Product post ID.
		 */
		do_action( 'wc_affiliate_save_product_commission_fields_before', $post_id );

		$product = wc_get_product( $post_id );

		if ( ! $product->is_type( 'simple' ) ) {
			return;
		}

		$fields = array(
			'affiliate_commission',
			'commission_type',
			'commission_amount',
			'customer_discount',
			'discount_type',
			'discount_amount',
		);

		foreach ( $fields as $field ) {
			if ( isset( $_POST[ $field ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
				$product->update_meta_data( $field, sanitize_text_field( wp_unslash( $_POST[ $field ] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
			}
		}

		$product->save();

		/**
		 * Action hook fired after saving product commission fields.
		 *
		 * @param int        $post_id Product post ID.
		 * @param WC_Product $product Product object.
		 */
		do_action( 'wc_affiliate_save_product_commission_fields_after', $post_id, $product );
	}

	/**
	 * Display commission fields for WooCommerce product variations.
	 *
	 * @param int    $loop           Variation loop index.
	 * @param array  $variation_data Variation data.
	 * @param object $variation      Variation object.
	 */
	public function show_variation_commission_fields( int $loop, array $variation_data, object $variation ) {
		echo wca_get_template( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			'variation-commission-fields',
			'views/admin/product',
			array(
				'loop'           => $loop,
				'variation_data' => $variation_data,
				'variation'      => $variation,
			)
		);
	}

	/**
	 * Save commission fields for WooCommerce product variations.
	 *
	 * @param int $variation_id Variation ID.
	 * @param int $loop         Variation loop index.
	 */
	public function save_variation_commission_fields( int $variation_id, $loop ) {
		$variation = wc_get_product( $variation_id );
		$fields    = array(
			'affiliate_commission',
			'commission_type',
			'commission_amount',
			'customer_discount',
			'discount_type',
			'discount_amount',
		);

		foreach ( $fields as $field ) {
			if ( isset( $_POST[ $field ][ $loop ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
				$variation->update_meta_data( $field, sanitize_text_field( wp_unslash( $_POST[ $field ][ $loop ] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
			}
		}

		$variation->save();
	}

	/**
	 * Update referral status when WooCommerce order status changes.
	 *
	 * @param int      $order_id    Order ID.
	 * @param string   $status_from Previous order status.
	 * @param string   $status_to   New order status.
	 * @param WC_Order $order       Order object.
	 */
	public function update_referral_status( int $order_id, string $status_from, string $status_to, WC_Order $order ) {
		/**
		 * Action hook fired before updating referral status.
		 *
		 * @param int      $order_id    Order ID.
		 * @param string   $status_from Previous order status.
		 * @param string   $status_to   New order status.
		 * @param WC_Order $order       Order object.
		 */
		do_action( 'wc_affiliate_update_referral_status_before', $order_id, $status_from, $status_to, $order );

		global $wpdb;
		$referral_status = wca_get_option( 'basic', "referral_status.{$status_to}", 'pending' );

		$wpdb->update(
			$wpdb->prefix . 'wca_referrals',
			array( 'payment_status' => $referral_status ),
			array(
				'order_id'       => $order_id,
				'transaction_id' => 0,
			)
		);

		/**
		 * Action hook fired after updating referral status.
		 *
		 * @param int      $order_id        Order ID.
		 * @param string   $status_from     Previous order status.
		 * @param string   $status_to       New order status.
		 * @param WC_Order $order           Order object.
		 * @param string   $referral_status Updated referral status.
		 */
		do_action( 'wc_affiliate_update_referral_status_after', $order_id, $status_from, $status_to, $order, $referral_status );
	}

	/**
	 * Handle WooCommerce Subscriptions renewal payments.
	 *
	 * Processes recurring commissions for subscription renewals.
	 *
	 * @param WC_Subscription $subscription Subscription object.
	 * @param WC_Order        $last_order   Last renewal order.
	 */
	public function handle_subscription_renewal( WC_Subscription $subscription, WC_Order $last_order ) {
		// Check if recurring commissions are enabled.
		if ( ! wca_is_recurring_order_enabled() ) {
			return;
		}

		global $wpdb;

		$order_id = $last_order->get_id();
		$order    = wc_get_order( $order_id );

		// Edge case: Skip commission for failed renewal payments.
		if ( 'failed' === $order->get_status() ) {
			return;
		}

		// Edge case: Check subscription status - only process active subscriptions.
		if ( 'active' !== $subscription->get_status() ) {
			return;
		}

		$payment_status   = 'pending';
		$order_total      = $last_order->get_total();
		$items            = $order->get_items();
		$total_commission = 0;

		// Look up the affiliate from the original (parent) order referral.
		// WCS passes the subscription object directly, so get_parent_id() gives
		// us the original order without an extra DB round-trip.
		$parent_order_id = $subscription->get_parent_id();
		$affiliate_id    = 0;

		if ( $parent_order_id ) {
			$referral_rows = $wpdb->get_results(
				$wpdb->prepare(
					"SELECT * FROM {$wpdb->prefix}wca_referrals WHERE order_id = %d LIMIT 1",
					$parent_order_id
				)
			);

			if ( ! empty( $referral_rows ) ) {
				$affiliate_id = $referral_rows[0]->affiliate;
			}
		}

		if ( ! $affiliate_id ) {
			return; // No affiliate found for this subscription.
		}

		// Collect products from order.
		$products = array();
		foreach ( $items as $item ) {
			if ( ! $item instanceof WC_Order_Item_Product ) {
				continue;
			}

			$products[ $item->get_product_id() ] = $item->get_name();
		}

		// Edge case: Use affiliate-specific recurring commission calculation.
		$commission_model = new \WC_Affiliate\Models\Commission();
		$total_commission = $commission_model->calculate_recurring_commission( $order, $affiliate_id );

		// Edge case: Skip commission for zero-amount renewals (free trials, etc.).
		if ( $total_commission <= 0 ) {
			return;
		}

		// Insert recurring commission.
		wca_insert_credit(
			$affiliate_id,
			$total_commission,
			'recurring',
			0,
			$order_id,
			$products,
			$order_total,
			$payment_status
		);
	}

	/**
	 * Add post state for affiliate dashboard page.
	 *
	 * @param array $post_states Existing post states.
	 * @param mixed $post        Post object.
	 *
	 * @return array Modified post states.
	 */
	public function add_dashboard_post_state( array $post_states, $post ): array {
		$dashboard_page_id = (int) wca_get_dashboard( 'id' );

		if ( $post instanceof WP_Post && 'page' === $post->post_type && $post->ID === $dashboard_page_id ) {
			$post_states['wc_affiliate_dashboard'] = __( 'My Affiliate Dashboard', 'wc-affiliate' );
		}

		return apply_filters( 'wc_affiliate_dashboard_post_states', $post_states, $post );
	}
}
