<?php
/**
 * Fast Events KML 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\JsonUtil;
use FeData\FastEvents\Utilities\UniqIds;
use stdClass;
use WP_Error;
use WP_Filesystem_Direct;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Fast Events kml admin class.
 */
class KmlAdminController {

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

	/**
	 * Fast Events kml 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 {

		// Upload KML file(s).
		register_rest_route(
			self::NAMESPACE,
			self::EVENTS_ROUTE . 'kml/(?P<id>\d+)',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'kml_upload' ),
				'permission_callback' => function () {
					return $this->auth->authorize( $this->auth::EVENT_TRACKING_UPLOAD );
				},
			)
		);
	}


	/**
	 * Process the kml content.
	 *
	 * @since 1.0
	 *
	 * @param string $kml KML content.
	 *
	 * @return stdClass|WP_Error|null JSON encode string.
	 */
	private function process_kml_content( string &$kml ): WP_Error|stdClass|null {
		if ( '' === $kml ) {
			return null;
		}

		// Load the XML content from the string.
		$xml = simplexml_load_string( $kml );
		if ( false === $xml ) {
			return new WP_Error( 'invalid_kml_content', __( 'Invalid KML content', 'fast-events' ), array( 'status' => 400 ) );
		}

		// phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
		$track_name = '';
		$points     = array();
		$legend     = array();
		$line       = new stdClass();
		foreach ( $xml->Document->Folder as $folder ) {
			foreach ( $folder->Placemark as $item ) {
				if ( isset( $item->Point ) ) {
					$point                = new stdClass();
					$point->id            = UniqIds::uniq_int();
					$point->name          = (string) $item->name;
					$style                = explode( '-', (string) $item->styleUrl );
					$point->symbol        = $style[1];
					$legend[ $style[1] ]  = (string) $folder->name;
					$point->color         = '#' . strtolower( $style[2] );
					$point->description   = (string) $item->description;
					$point->is_checkpoint = false;
					$latlong              = array_map( 'floatval', explode( ',', trim( (string) $item->Point->coordinates ) ) );
					unset( $latlong[2] );
					$point->coordinates = $latlong;
					$points[]           = $point;

				} elseif ( isset( $item->LineString ) ) {
					$line->id     = UniqIds::uniq_int();
					$line->name   = (string) $item->name;
					$track_name   = (string) $item->name;
					$style        = explode( '-', (string) $item->styleUrl );
					$line->color  = '#' . strtolower( $style[1] );
					$multi_points = explode( ' ', preg_replace( '!\s+!', ' ', str_replace( '\n\r\t\v\0', '', trim( (string) $item->LineString->coordinates ) ) ) );
					$coordinates  = array();
					foreach ( $multi_points as $single_point ) {
						$latlong = array_map( 'floatval', explode( ',', $single_point ) );
						unset( $latlong[2] );
						$coordinates[] = $latlong;
					}
					$line->coordinates = $coordinates;
				}
			}
			// phpcs:enable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
		}

		$track         = new stdClass();
		$track->id     = UniqIds::uniq_int();
		$track->name   = $track_name;
		$track->points = $points;
		$track->line   = $line;
		$track->legend = $legend;

		return ( $track );
	}


	/**
	 * Add KML-files to an existing event
	 *
	 * @since 1.0
	 * @since 1.4   Event authorization restriction
	 * @since 2.3.0 Use fast_events_json_encode.
	 *
	 * @param WP_REST_Request $data Request instance.
	 *
	 * @return WP_Error|WP_REST_Response|stdClass|null
	 */
	public function kml_upload( WP_REST_Request $data ): WP_Error|WP_REST_Response|stdClass|null {
		global $wpdb;

		// Check the event authorization.
		$event_id = absint( $data['id'] );
		$resp     = $this->auth->check_event_authorization( $event_id );
		if ( is_wp_error( $resp ) ) {
			return $resp;
		}

		$fs = new WP_Filesystem_Direct( false );

		// Are there any files?
		if ( ! isset( $_FILES['file']['tmp_name'] ) ) { // phpcs:ignore
			return new WP_Error( 'file_error', __( 'No KML-files found for upload', 'fast-events' ), array( 'status' => 400 ) );
		}

		// Loop all files.
		$count  = count( $_FILES['file']['tmp_name'] ); // phpcs:ignore
		$tracks = array();
		for ( $idx = 0; $idx < $count; $idx++ ) {

			// Read the file.
			$fname   = sanitize_text_field( wp_unslash( $_FILES['file']['tmp_name'][ $idx ] ) ); // phpcs:ignore
			$content = $fs->get_contents( $fname );

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

			// Process the file.
			$result = $this->process_kml_content( $content );
			if ( is_wp_error( $result ) ) {
				return $result;
			} elseif ( ! is_null( $result ) ) {
				$tracks[] = $result;
			}
		}

		// Processing done -> update event.
		$wpdb->query( 'START TRANSACTION' );

		$event = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT event_name, event_date, tracking_info FROM {$wpdb->prefix}fe_events WHERE id = %d FOR UPDATE",
				$event_id
			)
		);
		if ( ! $event ) {
			$wpdb->query( 'ROLLBACK' );
			return new WP_Error( 'not_found', __( 'Event not found', 'fast-events' ), array( 'status' => 404 ) );
		}

		// Get the 'old' tracking info.
		$json = json_decode( $event->tracking_info );
		if ( null === $json ) {
			$json = new stdClass();
		}
		$json->tracks = $tracks;

		// Update the tracking_info.
		$wpdb->update( $wpdb->prefix . 'fe_events', array( 'tracking_info' => JsonUtil::fast_events_json_encode( $json ) ), array( 'id' => $event_id ) );
		$wpdb->query( 'COMMIT' );

		/* translators: %1$d is the number of KML-files and %2$d is the event id */
		$message      = sprintf( __( 'Processed %1$d KML-file(s) for eventid #%2$d', 'fast-events' ), $count, $event_id );
		$tracks_added = implode( ', ', array_column( $json->tracks ?? array(), 'name' ) );
		return rest_ensure_response(
			array(
				'message' => $message,
				'tracks'  => $tracks_added,
			)
		);
	}
}
