<?php

namespace WC_Affiliate\API;

defined( 'ABSPATH' ) || exit;

use WC_Affiliate\Models\Affiliate;
use WC_Affiliate\Models\Visit as VisitModel;
use WC_Affiliate\Traits\Auth;
use WC_Affiliate\Traits\Date;
use WC_Affiliate\Traits\Limiter;
use WC_Affiliate\Traits\Rest;
use WC_Affiliate\Traits\Validation;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;

/**
 * Visit API Controller
 *
 * Handles REST API requests for visit tracking and management including
 * retrieving visit data, statistics, and analytics. Provides endpoints for
 * monitoring affiliate traffic and conversion tracking.
 *
 * @since   1.0.0
 * @package WC_Affiliate\API
 */
class Visit {

	use Auth;
	use Date;
	use Limiter;
	use Rest;
	use Validation;

	/**
	 * Get visits list.
	 *
	 * @param WP_REST_Request $request The REST request object.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function list( WP_REST_Request $request ) {
		$pagination     = $this->get_pagination_params( $request );
		$affiliate_id   = (int) $request->get_param( 'affiliate_id' );
		$converted_only = (bool) $request->get_param( 'converted_only' );
		$start_date     = sanitize_text_field( $request->get_param( 'start_date' ) );
		$end_date       = sanitize_text_field( $request->get_param( 'end_date' ) );
		$campaign       = sanitize_text_field( $request->get_param( 'campaign' ) );
		$search         = sanitize_text_field( $request->get_param( 'search' ) );

		$args = array(
			'limit'   => $pagination['limit'],
			'offset'  => $pagination['offset'],
			'orderby' => 'time',
			'order'   => 'DESC',
		);

		if ( $affiliate_id ) {
			$args['affiliate_id'] = $affiliate_id;
		}

		if ( $start_date ) {
			$args['start_date'] = $start_date;
		}

		if ( $end_date ) {
			$args['end_date'] = $end_date;
		}

		if ( $converted_only ) {
			$args['converted_only'] = true;
		}

		if ( $campaign ) {
			$args['campaign'] = $campaign;
		}

		if ( $search ) {
			$args['search'] = $search;
		}

		$visit_model  = new VisitModel();
		$visits       = $visit_model->get_visits( $args );
		$total_visits = $visit_model->get_count( $args );

		// Format response data.
		$data = array();
		foreach ( $visits as $visit ) {
			$data[] = $visit->to_array();
		}

		/**
		 * Filter visits list data before response.
		 *
		 * Allows modification of the visits list data.
		 *
		 * @since 1.0.0
		 *
		 * @param array           $data    Array of visit data.
		 * @param array           $args    Query arguments.
		 * @param WP_REST_Request $request The original request object.
		 */
		$data = apply_filters( 'wc_affiliate_rest_api_visits_list_data', $data, $args, $request );

		$response = $this->response_success( $data );

		return $this->add_pagination_headers( $response, $total_visits, $pagination['page'], $pagination['per_page'] );
	}

	/**
	 * Track new visit (dashboard endpoint).
	 *
	 * @param WP_REST_Request $request The REST request object.
	 *
	 * @return WP_REST_Response|\WP_Error
	 */
	public function track_visit( WP_REST_Request $request ) {
		$affiliate_id = (int) $request->get_param( 'affiliate_id' );
		$page_url     = sanitize_text_field( $request->get_param( 'page_url' ) );
		$referrer_url = sanitize_text_field( $request->get_param( 'referrer_url' ) );
		$campaign     = sanitize_text_field( $request->get_param( 'campaign' ) );

		// Validate affiliate exists and is active.
		$affiliate = new Affiliate( $affiliate_id );
		if ( ! $affiliate->exists() || $affiliate->get_status() !== 'active' ) {
			return $this->response_error( __( 'Invalid or inactive affiliate', 'wc-affiliate' ) );
		}

		// Track the visit.
		$visit_model = new VisitModel();
		$visit       = $visit_model->track( $affiliate_id, $page_url, $referrer_url, $campaign );

		if ( is_wp_error( $visit ) ) {
			return $this->response_error( $visit->get_error_message(), 400 );
		}

		// Return minimal data for security (dashboard endpoint).
		return $this->response_success(
			array(
				'visit_id'  => $visit->get_id(),
				'tracked'   => true,
				'timestamp' => current_time( 'mysql' ),
			),
			__( 'Visit tracked successfully', 'wc-affiliate' ),
			201
		);
	}

	/**
	 * Get visit statistics.
	 *
	 * @param WP_REST_Request $request The REST request object.
	 */
	public function get_visit_stats( WP_REST_Request $request ) {
		$affiliate_id = (int) $request->get_param( 'affiliate_id' );
		$start_date   = sanitize_text_field( $request->get_param( 'start_date' ) );
		$end_date     = sanitize_text_field( $request->get_param( 'end_date' ) );
		$period       = sanitize_text_field( $request->get_param( 'period' ) );

		// Check permissions.
		$current_user_id = get_current_user_id();
		if ( ! current_user_can( 'manage_options' ) && $affiliate_id ) {
			$affiliate = new Affiliate( $affiliate_id );
			if ( ! $affiliate->exists() || $affiliate->user_id !== $current_user_id ) {
				return $this->response_error( __( 'Permission denied', 'wc-affiliate' ), 403 );
			}
		}

		$args = array();

		if ( $affiliate_id ) {
			$args['affiliate_id'] = $affiliate_id;
		}

		// Handle period-based date ranges.
		if ( $period ) {
			$date_range         = $this->get_date_range( $period, $start_date, $end_date );
			$args['start_date'] = $date_range['start_date'];
			$args['end_date']   = $date_range['end_date'];
		} else {
			if ( $start_date ) {
				$args['start_date'] = $start_date;
			}
			if ( $end_date ) {
				$args['end_date'] = $end_date;
			}
		}

		$visit_model = new VisitModel();
		$stats       = $visit_model->get_statistics( $args );

		return $this->response_success( $stats );
	}

	/**
	 * Get top pages by visits.
	 *
	 * @param WP_REST_Request $request The REST request object.
	 */
	public function get_top_pages( WP_REST_Request $request ) {
		$affiliate_id = (int) $request->get_param( 'affiliate_id' );
		$limit        = min( 50, max( 5, (int) $request->get_param( 'limit' ) ) );
		$days         = max( 1, (int) $request->get_param( 'days' ) );

		// Check permissions.
		$current_user_id = get_current_user_id();
		if ( ! current_user_can( 'manage_options' ) && $affiliate_id ) {
			$affiliate = new Affiliate( $affiliate_id );
			if ( ! $affiliate->exists() || $affiliate->user_id !== $current_user_id ) {
				return $this->response_error( __( 'Permission denied', 'wc-affiliate' ), 403 );
			}
		} elseif ( ! current_user_can( 'manage_options' ) && ! $affiliate_id ) {
			// Non-admin users must specify their affiliate ID.
			$affiliate_model = new Affiliate();
			$user_affiliate  = $affiliate_model->get_by_user_id( $current_user_id );
			if ( ! $user_affiliate || ! $user_affiliate->exists() ) {
				return $this->response_error( __( 'Permission denied', 'wc-affiliate' ), 403 );
			}
			$affiliate_id = $user_affiliate->user_id;
		}

		$visit_model = new VisitModel();
		$top_pages   = $visit_model->get_top_pages( $limit, $affiliate_id, $days );

		return $this->response_success(
			array(
				'affiliate_id' => $affiliate_id,
				'period_days'  => $days,
				'pages'        => $top_pages,
			)
		);
	}

	/**
	 * Get top referrers.
	 *
	 * @param WP_REST_Request $request The REST request object.
	 */
	public function get_top_referrers( WP_REST_Request $request ) {
		$affiliate_id = (int) $request->get_param( 'affiliate_id' );
		$limit        = min( 50, max( 5, (int) $request->get_param( 'limit' ) ) );
		$days         = max( 1, (int) $request->get_param( 'days' ) );

		// Check permissions.
		$current_user_id = get_current_user_id();
		if ( ! current_user_can( 'manage_options' ) && $affiliate_id ) {
			$affiliate = new Affiliate( $affiliate_id );
			if ( ! $affiliate->exists() || $affiliate->user_id !== $current_user_id ) {
				return $this->response_error( __( 'Permission denied', 'wc-affiliate' ), 403 );
			}
		} elseif ( ! current_user_can( 'manage_options' ) && ! $affiliate_id ) {
			// Non-admin users must specify their affiliate ID.
			$affiliate_model = new Affiliate();
			$user_affiliate  = $affiliate_model->get_by_user_id( $current_user_id );
			if ( ! $user_affiliate || ! $user_affiliate->exists() ) {
				return $this->response_error( __( 'Permission denied', 'wc-affiliate' ), 403 );
			}
			$affiliate_id = $user_affiliate->user_id;
		}

		$visit_model   = new VisitModel();
		$top_referrers = $visit_model->get_top_referrers( $limit, $affiliate_id, $days );

		return $this->response_success(
			array(
				'affiliate_id' => $affiliate_id,
				'period_days'  => $days,
				'referrers'    => $top_referrers,
			)
		);
	}

	/**
	 * Get single visit details.
	 *
	 * @param WP_REST_Request $request The REST request object.
	 */
	public function get_visit( WP_REST_Request $request ) {
		$id    = (int) $request->get_param( 'visit_id' );
		$visit = new VisitModel( $id );

		if ( ! $visit->exists() ) {
			return $this->response_error( __( 'Visit not found', 'wc-affiliate' ), 404 );
		}

		return $this->response_success( $visit->to_array() );
	}

	/**
	 * Delete visit (admin only)
	 */
	public function delete_visit( WP_REST_Request $request ) {
		$id    = (int) $request->get_param( 'visit_id' );
		$visit = new VisitModel( $id );

		if ( ! $visit->exists() ) {
			return $this->response_error( __( 'Visit not found', 'wc-affiliate' ), 404 );
		}

		$result = $visit->delete();

		if ( is_wp_error( $result ) ) {
			return $this->response_error( $result->get_error_message(), 400 );
		}

		return $this->response_success(
			null,
			__( 'Visit deleted successfully', 'wc-affiliate' )
		);
	}

	/**
	 * Clean up old visits (admin only)
	 */
	public function cleanup_visits( WP_REST_Request $request ) {
		if ( ! current_user_can( 'manage_options' ) ) {
			return $this->response_error( __( 'Permission denied', 'wc-affiliate' ), 403 );
		}

		$days          = max( 30, (int) $request->get_param( 'days' ) );
		$visit_model   = new VisitModel();
		$deleted_count = $visit_model->cleanup_old_visits( $days );

		return $this->response_success(
			array( 'deleted_count' => $deleted_count ),
			sprintf(
			/* translators: %d is the number of visits cleaned up */
				__( '%d old visits cleaned up successfully', 'wc-affiliate' ),
				$deleted_count
			)
		);
	}

	/**
	 * Get visit trends over time
	 */
	public function get_visit_trends( WP_REST_Request $request ) {
		$affiliate_id = (int) $request->get_param( 'affiliate_id' );
		$period       = sanitize_text_field( $request->get_param( 'period' ) );
		$group_by     = sanitize_text_field( $request->get_param( 'group_by' ) );

		// Check permissions
		$current_user_id = get_current_user_id();
		if ( ! current_user_can( 'manage_options' ) && $affiliate_id ) {
			$affiliate = new Affiliate( $affiliate_id );
			if ( ! $affiliate->exists() || $affiliate->user_id !== $current_user_id ) {
				return $this->response_error( __( 'Permission denied', 'wc-affiliate' ), 403 );
			}
		}

		$date_range = $this->get_date_range( $period, '', '' );

		// Use Visit model to get chart data
		$visit_model = new \WC_Affiliate\Models\Visit();
		$chart_data  = $visit_model->get_chart_data( $date_range, $group_by, $affiliate_id );

		return $this->response_success(
			array(
				'period'       => $period,
				'group_by'     => $group_by,
				'affiliate_id' => $affiliate_id,
				'trends'       => $chart_data,
			)
		);
	}

	/**
	 * Get SQL group format for trends
	 */
	protected function get_group_format( $group_by ): string {
		// Use Cleaner trait to sanitize input
		$group_by = sanitize_text_field( $group_by );

		switch ( $group_by ) {
			case 'hour':
				return "DATE_FORMAT(FROM_UNIXTIME(time), '%Y-%m-%d %H:00:00')";
			case 'day':
				return "DATE_FORMAT(FROM_UNIXTIME(time), '%Y-%m-%d')";
			case 'week':
				return "DATE_FORMAT(FROM_UNIXTIME(time), '%Y-%u')";
			case 'month':
				return "DATE_FORMAT(FROM_UNIXTIME(time), '%Y-%m')";
			default:
				return "DATE_FORMAT(FROM_UNIXTIME(time), '%Y-%m-%d')";
		}
	}

	/**
	 * Get visit conversion counts
	 *
	 * @param WP_REST_Request $request REST request object containing optional filter parameters.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function get_conversion_counts( WP_REST_Request $request ) {
		$affiliate_id = (int) $request->get_param( 'affiliate_id' );
		$start_date   = sanitize_text_field( $request->get_param( 'start_date' ) );
		$end_date     = sanitize_text_field( $request->get_param( 'end_date' ) );
		$campaign     = sanitize_text_field( $request->get_param( 'campaign' ) );

		$args = array();

		// Check permissions and get authorized affiliate ID.
		if ( $affiliate_id ) {
			$authorized_affiliate_id = $this->check_affiliate_access( $affiliate_id, $request );
			if ( is_wp_error( $authorized_affiliate_id ) ) {
				return $this->response_error( $authorized_affiliate_id->get_error_message(), 403 );
			}

			if ( $authorized_affiliate_id ) {
				$args['affiliate_id'] = $authorized_affiliate_id;
			}
		}

		if ( $start_date ) {
			$args['start_date'] = $start_date;
		}

		if ( $end_date ) {
			$args['end_date'] = $end_date;
		}

		if ( $campaign ) {
			$args['campaign'] = $campaign;
		}

		$visit_model       = new VisitModel();
		$conversion_counts = $visit_model->get_conversion_counts( $args );

		return $this->response_success( $conversion_counts );
	}

	/**
	 * Get all visit conversion counts (admin only)
	 *
	 * @param WP_REST_Request $request REST request object containing optional filter parameters.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function get_admin_conversion_counts( WP_REST_Request $request ) {
		// Admin check.
		if ( ! current_user_can( 'manage_options' ) ) {
			return $this->response_error( __( 'Permission denied', 'wc-affiliate' ), 403 );
		}

		$start_date = sanitize_text_field( $request->get_param( 'start_date' ) );
		$end_date   = sanitize_text_field( $request->get_param( 'end_date' ) );
		$campaign   = sanitize_text_field( $request->get_param( 'campaign' ) );

		$args = array();

		if ( $start_date ) {
			$args['start_date'] = $start_date;
		}

		if ( $end_date ) {
			$args['end_date'] = $end_date;
		}

		if ( $campaign ) {
			$args['campaign'] = $campaign;
		}

		$visit_model       = new VisitModel();
		$conversion_counts = $visit_model->get_conversion_counts( $args );

		return $this->response_success( $conversion_counts );
	}
}
