<?php
/**
 * Fast Events Coupons Admin controller.
 *
 * @since 1.3
 */

declare( strict_types=1 );
namespace FeData\FastEvents\RestAPI;

defined( 'ABSPATH' ) || exit;

use FeData\FastEvents\Helpers\AuthHelper;
use FeData\FastEvents\Utilities\TimeUtil;
use stdClass;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Fast Events coupons admin class.
 */
class CouponsAdminController {

	private const NAMESPACE     = 'fast-events/v1';
	private const COUPONS_ROUTE = '/admin/coupons/';

	/**
	 * Fast Events coupons admin constructor.
	 *
	 * @param AuthHelper $auth Auth helper.
	 *
	 * @since 1.3
	 */
	public function __construct( private readonly AuthHelper $auth ) {}


	/**
	 * Register the routes for the objects of the controller.
	 *
	 * @since 1.3
	 */
	public function register_routes(): void {

		// Get all coupons.
		register_rest_route(
			self::NAMESPACE,
			self::COUPONS_ROUTE . 'all',
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( $this, 'all_coupons' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::COUPON_READ );
				},
			)
		);

		// Import coupons.
		register_rest_route(
			self::NAMESPACE,
			self::COUPONS_ROUTE . 'import',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'import_coupons' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::COUPON_IMPORT );
				},
				'args'                => array(
					'header' => array(
						'required'          => true,
						'type'              => 'integer',
						'sanitize_callback' => 'absint',
						'enum'              => array( 0, 1 ),
					),
				),
			)
		);

		// Delete expired coupons.
		register_rest_route(
			self::NAMESPACE,
			self::COUPONS_ROUTE . 'expired',
			array(
				'methods'             => WP_REST_Server::DELETABLE,
				'callback'            => array( $this, 'delete_expired_coupons' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::COUPON_IMPORT );
				},
			)
		);
	}


	/**
	 * Get all coupons
	 *
	 * @since 2.2.0
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function all_coupons(): WP_Error|WP_REST_Response {
		global $wpdb;

		return rest_ensure_response( $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}fe_event_coupons" ) );
	}


	/**
	 * Import CSV-file with coupons.
	 *
	 * @since 2.2.0
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
	 */
	public function import_coupons( WP_REST_Request $request ): WP_Error|WP_REST_Response {
		global $wpdb;

		if ( isset( $_FILES['file']['tmp_name'] ) && empty( $_FILES['file']['error'] ) ) { // phpcs:ignore

			// Read the file.
			$fname = sanitize_text_field( wp_unslash( $_FILES['file']['tmp_name'] ) ); // phpcs:ignore
			$arr   = file( $fname, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );

			// Remove temp file.
			wp_delete_file( $fname );

			// Create the query.
			$values = array();
			$query  = "INSERT INTO {$wpdb->prefix}fe_event_coupons (code,description," .
					'type,amount,usage_limit,usage_limit_per_user,' .
					'start_date,end_date,events,tickets,minimum_tickets,maximum_tickets,' .
					'minimum_amount,maximum_amount,email) VALUES ';
			$cnt    = 0;
			$mask   = ' "';
			foreach ( $arr as $items ) {
				if ( 0 === $cnt && 1 === $request['header'] ) {
					++$cnt;
					continue;
				}
				$element = explode( ';', $items );
				if ( 19 !== count( $element ) ) {
					/* translators: %1$d is a line number */
					return new WP_Error( 'invalid_columns', sprintf( __( 'Invalid number of columns in line #%1$d', 'fast-events' ), $cnt + 1 ), array( 'status' => 400 ) );
				}
				if ( ! empty( trim( $element[12], $mask ) ) && ! preg_match( '/^\d+(?:,\d+)*$/', trim( $element[12], $mask ) ) ) {
					/* translators: %1$d is a line number */
					return new WP_Error( 'invalid_events', sprintf( __( 'Invalid events list in line #%1$d', 'fast-events' ), $cnt + 1 ), array( 'status' => 400 ) );
				}
				if ( ( ! empty( trim( $element[10], $mask ) ) && ! TimeUtil::validate_date( trim( $element[10], $mask ) ) ) ||
					( ! empty( trim( $element[11], $mask ) ) && ! TimeUtil::validate_date( trim( $element[11], $mask ) ) ) ) {
					/* translators: %1$d is a line number */
					return new WP_Error( 'invalid_dates', sprintf( __( 'Invalid dates in line #%1$d', 'fast-events' ), $cnt + 1 ), array( 'status' => 400 ) );
				}
				if ( ! empty( trim( $element[18], $mask ) ) && ! is_email( trim( $element[18], $mask ) ) ) {
					/* translators: %1$d is a line number */
					return new WP_Error( 'invalid_email', sprintf( __( 'Invalid emailaddress in line #%1$d', 'fast-events' ), $cnt + 1 ), array( 'status' => 400 ) );
				}
				$code        = '\'' . sanitize_text_field( trim( $element[0], $mask ) ) . '\'';
				$description = '\'' . sanitize_text_field( trim( $element[1], $mask ) ) . '\'';
				$type        = '\'' . sanitize_text_field( trim( $element[4], $mask ) ) . '\'';
				$amount      = sanitize_text_field( trim( $element[5], $mask ) );
				$usg_limit   = (string) absint( trim( $element[6], $mask ) );
				$usg_limit_u = (string) absint( trim( $element[7], $mask ) );
				$start_date  = '\'' . sanitize_text_field( trim( $element[10], $mask ) ) . '\'';
				$end_date    = '\'' . sanitize_text_field( trim( $element[11], $mask ) ) . '\'';
				$events      = sanitize_text_field( trim( $element[12], $mask ) );
				$tickets     = sanitize_text_field( trim( $element[13], $mask ) );
				$min_tickets = sanitize_text_field( trim( $element[14], $mask ) );
				$max_tickets = sanitize_text_field( trim( $element[15], $mask ) );
				$min_amount  = sanitize_text_field( trim( $element[16], $mask ) );
				$max_amount  = sanitize_text_field( trim( $element[17], $mask ) );
				$email       = sanitize_text_field( trim( $element[18], $mask ) );
				$events      = empty( $events ) ? 'null' : '\'' . $events . '\'';
				$tickets     = empty( $tickets ) ? 'null' : '\'' . $tickets . '\'';
				$min_tickets = empty( $min_tickets ) ? 'null' : (string) absint( $min_tickets );
				$max_tickets = empty( $max_tickets ) ? 'null' : (string) absint( $max_tickets );
				$min_amount  = empty( $min_amount ) ? 'null' : '\'' . $min_amount . '\'';
				$max_amount  = empty( $max_amount ) ? 'null' : '\'' . $max_amount . '\'';
				$email       = empty( $email ) ? 'null' : '\'' . $email . '\'';
				$values[]    = "($code,$description,$type,$amount,$usg_limit,$usg_limit_u,$start_date,$end_date,$events," .
							"$tickets,$min_tickets,$max_tickets,$min_amount,$max_amount,$email)";
				++$cnt;
			}
			if ( 0 === $cnt ) {
				return new WP_Error( 'no_coupons', __( 'No coupons found in file', 'fast-events' ), array( 'status' => 404 ) );
			}
			$query .= implode( ', ', $values );
			$query .= ' ON DUPLICATE KEY UPDATE code = VALUES(code)';

			// Add the entries to the coupons table.
			$result = $wpdb->query( $query ); // phpcs:ignore

			// Was there a SQL error?
			if ( false === $result ) {
				/* translators: %1$s is the mysql error string */
				return new WP_Error( 'not_found', sprintf( __( 'Error adding coupons (%1$s)', 'fast-events' ), $wpdb->last_error ), array( 'status' => 400 ) );
			}
			$item        = new stdClass();
			$item->total = 0 === $request['header'] ? $cnt : $cnt - 1;

			return rest_ensure_response( $item );

		} else {
			return new WP_Error( 'file_error', __( 'No file found for upload', 'fast-events' ), array( 'status' => 409 ) );
		}
	}


	/**
	 * Delete expired coupons.
	 *
	 * @since 2.2.0
	 *
	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
	 */
	public function delete_expired_coupons(): WP_Error|WP_REST_Response {
		global $wpdb;

		$result = $wpdb->query( "DELETE FROM {$wpdb->prefix}fe_event_coupons WHERE end_date < NOW()" );
		return rest_ensure_response( array( 'count' => (int) $result ) );
	}
}
