<?php
/**
 * DESCRIPTION: Event service handling event CRUD, validation, event type presets,
 * featured image handling, and event lifecycle dispatching.
 *
 * @package NemtlyBooking\Services
 */

namespace NemtlyBooking\Services;

use NemtlyBooking\Core\Event_Manager;
use NemtlyBooking\Models\Event;
use NemtlyBooking\Repositories\Event_Repository;
use NemtlyBooking\Services\Event_Types_Service;

class Event_Service {
    /**
     * Event repository
     *
     * @var Event_Repository
     */
    private $event_repository;

    /**
     * Event manager
     *
     * @var Event_Manager
     */
    private $event_manager;

    /**
     * Constructor
     *
     * @param Event_Repository $event_repository The event repository instance.
     * @param Event_Manager    $event_manager    The event manager instance.
     */
    public function __construct( Event_Repository $event_repository, Event_Manager $event_manager ) {
        $this->event_repository = $event_repository;
        $this->event_manager    = $event_manager;
    }

    /**
     * Create a new event
     *
     * @param array $data Event data.
     *
     * @return Event
     * @throws \Exception When event creation fails.
     */
    public function create_event( array $data ): Event {
        // Apply event type configuration if provided
        if ( ! empty( $data['event_type'] ) ) {
            $event_types_service = new Event_Types_Service();
            $data                = $event_types_service->apply_event_type_config( $data, $data['event_type'] );
        }

        // Validate event data
        $this->validate_event_data( $data );

        // Create event object
        $event = new Event(
            [
                'title'              => sanitize_text_field( $data['title'] ),
                'description'        => wp_kses_post( $data['description'] ?? '' ),
                'date'               => sanitize_text_field( $data['date'] ),
                'time'               => sanitize_text_field( $data['time'] ),
                'duration'           => intval( $data['duration'] ?? 60 ),
                'location'           => sanitize_text_field( $data['location'] ?? '' ),
                'max_attendees'      => intval( $data['max_attendees'] ?? 0 ),
                'booking_cutoff'     => intval( $data['booking_cutoff'] ?? 0 ),
                'allow_waitlist'     => (bool) ( $data['allow_waitlist'] ?? false ),
                'recurrence_pattern' => sanitize_text_field( $data['recurrence_pattern'] ?? 'none' ),
                'recurrence_count'   => intval( $data['recurrence_count'] ?? 1 ),
                'recurrence_end'     => sanitize_text_field( $data['recurrence_end'] ?? '' ),
                'parent_event_id'    => intval( $data['parent_event_id'] ?? 0 ),
                'is_recurring'       => (bool) ( $data['is_recurring'] ?? false ),
                'status'             => sanitize_text_field( $data['status'] ?? 'draft' ),
                'event_type'         => sanitize_text_field( $data['event_type'] ?? 'one-on-one' ),
                'buffer_before'      => intval( $data['buffer_before'] ?? 0 ),
                'buffer_after'       => intval( $data['buffer_after'] ?? 0 ),
                'price'              => floatval( $data['price'] ?? 0 ),
                'featured_image'     => $data['featured_image'] ?? '',
            ]
        );

        // Dispatch before create event
        $this->event_manager->dispatch( 'event.before_create', $event->to_array() );

        // Save event
        $saved_event = $this->event_repository->create( $event );
        if ( ! $saved_event ) {
            throw new \Exception( esc_html__( 'Failed to create event', 'nemtly-booking' ) );
        }

        // Dispatch after create event
        $this->event_manager->dispatch( 'event.created', $saved_event->to_array() );

        return $saved_event;
    }

    /**
     * Update an event
     *
     * @param int   $id   Event ID.
     * @param array $data Event data.
     *
     * @return Event
     * @throws \Exception When event update fails.
     */
    public function update_event( int $id, array $data ): Event {
        // Find existing event
        $event = $this->event_repository->find( $id );
        if ( ! $event ) {
            throw new \Exception( esc_html__( 'Event not found', 'nemtly-booking' ) );
        }

        // DEBUG: trace the date keys reaching the service and confirm the UTC
        // setters now persist them (previously date_utc/time_utc were dropped).
        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
            error_log(
                '[nemtly-debug] Event_Service::update_event received → ' . wp_json_encode(
                    [
                        'id'                  => $id,
                        'date'                => $data['date'] ?? null,
                        'time'                => $data['time'] ?? null,
                        'end_date'            => $data['end_date'] ?? null,
                        'end_time'            => $data['end_time'] ?? null,
                        'date_utc'            => $data['date_utc'] ?? null,
                        'time_utc'            => $data['time_utc'] ?? null,
                        'has_date_utc_setter' => true,
                    ]
                )
            );
        }

        // Validate event data (partial validation for updates)
        $this->validate_event_data( $data, true );

        // Update event properties
        if ( isset( $data['title'] ) ) {
            $event->set_title( sanitize_text_field( $data['title'] ) );
        }
        if ( isset( $data['description'] ) ) {
            $event->set_description( wp_kses_post( $data['description'] ) );
        }
        if ( isset( $data['date'] ) ) {
            $event->set_date( sanitize_text_field( $data['date'] ) );
        }
        if ( isset( $data['time'] ) ) {
            $event->set_time( sanitize_text_field( $data['time'] ) );
        }
        if ( isset( $data['date_utc'] ) ) {
            $event->set_date_utc( sanitize_text_field( $data['date_utc'] ) );
        }
        if ( isset( $data['time_utc'] ) ) {
            $event->set_time_utc( sanitize_text_field( $data['time_utc'] ) );
        }
        if ( isset( $data['timezone'] ) ) {
            $event->set_timezone( sanitize_text_field( $data['timezone'] ) );
        }
        if ( isset( $data['end_date'] ) ) {
            $event->set_end_date( sanitize_text_field( $data['end_date'] ) );
        }
        if ( isset( $data['end_time'] ) ) {
            $event->set_end_time( sanitize_text_field( $data['end_time'] ) );
        }
        if ( isset( $data['end_date_utc'] ) ) {
            $event->set_end_date_utc( sanitize_text_field( $data['end_date_utc'] ) );
        }
        if ( isset( $data['end_time_utc'] ) ) {
            $event->set_end_time_utc( sanitize_text_field( $data['end_time_utc'] ) );
        }
        if ( isset( $data['duration'] ) ) {
            $event->set_duration( intval( $data['duration'] ) );
        }
        if ( isset( $data['location'] ) ) {
            $event->set_location( sanitize_text_field( $data['location'] ) );
        }
        if ( isset( $data['max_attendees'] ) ) {
            $event->set_max_attendees( intval( $data['max_attendees'] ) );
        }
        if ( isset( $data['booking_cutoff'] ) ) {
            $event->set_booking_cutoff( intval( $data['booking_cutoff'] ) );
        }
        if ( isset( $data['allow_waitlist'] ) ) {
            $event->set_allow_waitlist( (bool) $data['allow_waitlist'] );
        }
        if ( isset( $data['recurrence_pattern'] ) ) {
            $event->set_recurrence_pattern( sanitize_text_field( $data['recurrence_pattern'] ) );
        }
        if ( isset( $data['recurrence_count'] ) ) {
            $event->set_recurrence_count( intval( $data['recurrence_count'] ) );
        }
        if ( isset( $data['recurrence_end'] ) ) {
            $event->set_recurrence_end( sanitize_text_field( $data['recurrence_end'] ) );
        }
        if ( isset( $data['parent_event_id'] ) ) {
            $event->set_parent_event_id( intval( $data['parent_event_id'] ) );
        }
        if ( isset( $data['is_recurring'] ) ) {
            $event->set_is_recurring( (bool) $data['is_recurring'] );
        }
        if ( isset( $data['status'] ) ) {
            $event->set_status( sanitize_text_field( $data['status'] ) );
        }
        if ( isset( $data['event_type'] ) ) {
            $event->set_event_type( sanitize_text_field( $data['event_type'] ) );
        }
        if ( isset( $data['availability'] ) ) {
            $event->set_availability( (array) $data['availability'] );
        }
        if ( isset( $data['styling'] ) ) {
            $event->set_styling( \NemtlyBooking\Core\Event_Creator::sanitize_styling( (array) $data['styling'] ) );
        }
        if ( isset( $data['buffer_before'] ) ) {
            $event->set_buffer_before( intval( $data['buffer_before'] ) );
        }
        if ( isset( $data['buffer_after'] ) ) {
            $event->set_buffer_after( intval( $data['buffer_after'] ) );
        }
        if ( isset( $data['price'] ) ) {
            $event->set_price( floatval( $data['price'] ) );
        }
        if ( isset( $data['featured_image'] ) ) {
            // Handle featured image using WordPress native functions
            $this->set_event_featured_image( $event->get_id(), $data['featured_image'] );
        }

        // Dispatch before update event
        $this->event_manager->dispatch( 'event.before_update', $event->to_array() );

        // Save event
        $updated_event = $this->event_repository->update( $event );
        if ( ! $updated_event ) {
            throw new \Exception( esc_html__( 'Failed to update event', 'nemtly-booking' ) );
        }

        // Dispatch after update event
        $this->event_manager->dispatch( 'event.updated', $updated_event->to_array() );

        return $updated_event;
    }

    /**
     * Set featured image for an event using WordPress native functions
     *
     * @param int    $event_id      Event ID.
     * @param string $image_url_or_id Image URL or attachment ID.
     *
     * @return bool|WP_Error True on success, WP_Error on failure.
     */
    private function set_event_featured_image( int $event_id, $image_url_or_id ) {
        if ( empty( $image_url_or_id ) ) {
            return delete_post_thumbnail( $event_id );
        }

        // If it's already an attachment ID
        if ( is_numeric( $image_url_or_id ) ) {
            $attachment_id = (int) $image_url_or_id;
        } else {
            // If it's a URL, we need to handle it
            $attachment_id = $this->handle_image_url( $image_url_or_id );
        }

        if ( is_wp_error( $attachment_id ) ) {
            return $attachment_id;
        }

        if ( $attachment_id ) {
            return set_post_thumbnail( $event_id, $attachment_id );
        }

        return false;
    }

    /**
     * Handle image URL and convert to attachment
     *
     * @param string $image_url Image URL (can be base64 data URL).
     *
     * @return int|WP_Error Attachment ID on success, WP_Error on failure.
     */
    private function handle_image_url( string $image_url ) {
        // For base64 data URLs, we need to decode and save as attachment
        if ( strpos( $image_url, 'data:image' ) === 0 ) {
            return $this->handle_base64_image( $image_url );
        }

        // Check if this URL belongs to an existing attachment in the media library
        $attachment_id = attachment_url_to_postid( $image_url );
        if ( $attachment_id ) {
            return $attachment_id;
        }

        return new \WP_Error( 'unsupported_image_url', __( 'External image URLs are not supported. Please upload the image through the media library.', 'nemtly-booking' ) );
    }

    /**
     * Handle base64 image data and convert to attachment
     *
     * @param string $base64_data Base64 image data.
     *
     * @return int|WP_Error Attachment ID on success, WP_Error on failure.
     */
    private function handle_base64_image( string $base64_data ) {
        // Only users who can upload files should be allowed to create attachments.
        if ( ! current_user_can( 'upload_files' ) && ! current_user_can( 'manage_options' ) ) {
            return new \WP_Error( 'forbidden', __( 'You do not have permission to upload files.', 'nemtly-booking' ), [ 'status' => 403 ] );
        }

        // Parse data URL: data:{mime};base64,{data}
        if ( ! preg_match( '#^data:(image/[a-z0-9.+-]+);base64,(.+)$#i', $base64_data, $matches ) ) {
            return new \WP_Error( 'invalid_base64', __( 'Invalid base64 image data.', 'nemtly-booking' ) );
        }

        $mime       = strtolower( $matches[1] );
        $b64        = $matches[2];
        $image_data = base64_decode( $b64 );

        if ( false === $image_data ) {
            return new \WP_Error( 'invalid_base64', __( 'Invalid base64 image data.', 'nemtly-booking' ) );
        }

        // Allowed image MIME types
        $allowed_mimes = [ 'image/jpeg', 'image/png', 'image/gif', 'image/webp' ];
        if ( ! in_array( $mime, $allowed_mimes, true ) ) {
            return new \WP_Error( 'invalid_mime', __( 'Unsupported image type.', 'nemtly-booking' ) );
        }

        // Enforce upload size limits
        if ( function_exists( 'wp_max_upload_size' ) ) {
            $max_bytes = wp_max_upload_size();
        } else {
            $max_bytes = 2 * 1024 * 1024; // 2MB fallback
        }

        if ( strlen( $image_data ) > $max_bytes ) {
            return new \WP_Error( 'image_too_large', __( 'Image exceeds the maximum upload size.', 'nemtly-booking' ) );
        }

        // Prepare filename and write via WP API
        $upload_dir = wp_upload_dir();
        if ( ! isset( $upload_dir['path'] ) ) {
            return new \WP_Error( 'upload_dir_error', __( 'Unable to determine upload directory.', 'nemtly-booking' ) );
        }

        // Ensure directory exists
        if ( ! wp_mkdir_p( $upload_dir['path'] ) ) {
            return new \WP_Error( 'upload_dir_error', __( 'Unable to create upload directory.', 'nemtly-booking' ) );
        }

        $ext_map  = [
			'image/jpeg' => 'jpg',
			'image/png'  => 'png',
			'image/gif'  => 'gif',
			'image/webp' => 'webp',
		];
        $ext      = isset( $ext_map[ $mime ] ) ? $ext_map[ $mime ] : 'jpg';
        $filename = 'nemtly-event-' . wp_unique_filename( $upload_dir['path'], uniqid() . '.' . $ext );

        // Use wp_upload_bits so WP handles file creation safely
        $file = wp_upload_bits( $filename, null, $image_data );
        if ( $file['error'] ) {
            return new \WP_Error( 'file_save_failed', $file['error'] );
        }

        $file_path = $file['file'];

        // Double-check file type and extension
        $checked = wp_check_filetype_and_ext( $file_path, $filename );
        if ( empty( $checked['ext'] ) || empty( $checked['type'] ) ) {
            wp_delete_file( $file_path );
            return new \WP_Error( 'invalid_file', __( 'Uploaded file type could not be verified.', 'nemtly-booking' ) );
        }

        // Prepare attachment
        $attachment = [
            'post_mime_type' => $checked['type'],
            'post_title'     => sanitize_file_name( pathinfo( $filename, PATHINFO_FILENAME ) ),
            'post_content'   => '',
            'post_status'    => 'inherit',
        ];

        // Insert attachment into the media library
        $attachment_id = wp_insert_attachment( $attachment, $file_path );
        if ( is_wp_error( $attachment_id ) ) {
            wp_delete_file( $file_path );
            return $attachment_id;
        }

        require_once ABSPATH . 'wp-admin/includes/image.php';
        $attachment_data = wp_generate_attachment_metadata( $attachment_id, $file_path );
        wp_update_attachment_metadata( $attachment_id, $attachment_data );

        return $attachment_id;
    }

    /**
     * Delete an event
     *
     * @param int $id Event ID.
     *
     * @return bool
     * @throws \Exception When event deletion fails.
     */
    public function delete_event( int $id ): bool {
        // Find existing event
        $event = $this->event_repository->find( $id );
        if ( ! $event ) {
            throw new \Exception( esc_html__( 'Event not found', 'nemtly-booking' ) );
        }
        // Dispatch before delete event
        $this->event_manager->dispatch( 'event.before_delete', $event->to_array() );
        // Delete event
        $result = $this->event_repository->delete( $id );
        if ( ! $result ) {
            throw new \Exception( esc_html__( 'Failed to delete event', 'nemtly-booking' ) );
        }
        // Dispatch after delete event
        $this->event_manager->dispatch( 'event.deleted', $event->to_array() );

        return true;
    }

    /**
     * Get event by ID
     *
     * @param int $id Event ID.
     *
     * @return Event|null
     */
    public function get_event( int $id ): ?Event {
        return $this->event_repository->find( $id );
    }

    /**
     * Get all events
     *
     * @param array $args Query arguments.
     *
     * @return Event[]
     */
    public function get_events( array $args = [] ): array {
        return $this->event_repository->find_all( $args );
    }

    /**
     * Get events by date range
     *
     * @param string $start_date Start date.
     * @param string $end_date   End date.
     *
     * @return Event[]
     */
    public function get_events_by_date_range( string $start_date, string $end_date ): array {
        return $this->event_repository->find_by_date_range( $start_date, $end_date );
    }

    /**
     * Validate event data
     *
     * @param array $data    Event data.
     * @param bool  $partial Whether to perform partial validation for updates.
     *
     * @throws \Exception When validation fails.
     */
    private function validate_event_data( array $data, bool $partial = false ): void {
        $errors = array();

        // Required fields for create (not required for partial updates)
        if ( ! $partial ) {
            if ( empty( $data['title'] ) ) {
                $errors[] = __( 'Event title is required', 'nemtly-booking' );
            }
            if ( empty( $data['date'] ) ) {
                $errors[] = __( 'Event date is required', 'nemtly-booking' );
            }
            if ( empty( $data['time'] ) ) {
                $errors[] = __( 'Event time is required', 'nemtly-booking' );
            }
        }

        // Validate title if provided
        if ( isset( $data['title'] ) && empty( $data['title'] ) ) {
            $errors[] = __( 'Event title cannot be empty', 'nemtly-booking' );
        }

        // Validate date if provided
        if ( isset( $data['date'] ) ) {
            if ( empty( $data['date'] ) ) {
                $errors[] = __( 'Event date cannot be empty', 'nemtly-booking' );
            } elseif ( ! $this->is_valid_date( $data['date'] ) ) {
                $errors[] = __( 'Invalid event date format', 'nemtly-booking' );
            }
        }

        // Validate time if provided
        if ( isset( $data['time'] ) ) {
            if ( empty( $data['time'] ) ) {
                $errors[] = __( 'Event time cannot be empty', 'nemtly-booking' );
            } elseif ( ! $this->is_valid_time( $data['time'] ) ) {
                $errors[] = __( 'Invalid event time format', 'nemtly-booking' );
            }
        }

        // Validate duration if provided
        if ( isset( $data['duration'] ) && ( intval( $data['duration'] ) < 15 || intval( $data['duration'] ) > 1440 ) ) {
            $errors[] = __( 'Event duration must be between 15 and 1440 minutes', 'nemtly-booking' );
        }

        // Validate max attendees if provided
        if ( isset( $data['max_attendees'] ) && intval( $data['max_attendees'] ) < 0 ) {
            $errors[] = __( 'Maximum attendees cannot be negative', 'nemtly-booking' );
        }

        // Validate booking cutoff if provided
        if ( isset( $data['booking_cutoff'] ) && intval( $data['booking_cutoff'] ) < 0 ) {
            $errors[] = __( 'Booking cutoff cannot be negative', 'nemtly-booking' );
        }

        // Validate recurrence count if provided (0 = no limit).
        if ( isset( $data['recurrence_count'] ) && ( intval( $data['recurrence_count'] ) < 0 || intval( $data['recurrence_count'] ) > 365 ) ) {
            $errors[] = __( 'Recurrence count must be between 0 and 365', 'nemtly-booking' );
        }

        // Validate recurrence end date if provided
        if ( isset( $data['recurrence_end'] ) && ! empty( $data['recurrence_end'] ) && ! $this->is_valid_date( $data['recurrence_end'] ) ) {
            $errors[] = __( 'Invalid recurrence end date format', 'nemtly-booking' );
        }

        // Validate status if provided
        if ( isset( $data['status'] ) ) {
            $allowed_statuses = [ 'draft', 'publish', 'private', 'trash' ];
            if ( ! in_array( $data['status'], $allowed_statuses, true ) ) {
                $errors[] = __( 'Invalid event status', 'nemtly-booking' );
            }
        }

        if ( ! empty( $errors ) ) {
            // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
            throw new \Exception( implode( ', ', $errors ) );
        }
    }

    /**
     * Validate date format
     *
     * @param string $date Date string.
     *
     * @return bool
     */
    private function is_valid_date( string $date ): bool {
        $d = \DateTime::createFromFormat( 'Y-m-d', $date );

        return $d && $d->format( 'Y-m-d' ) === $date;
    }

    /**
     * Validate time format
     *
     * @param string $time Time string.
     *
     * @return bool
     */
    private function is_valid_time( string $time ): bool {
        $t = \DateTime::createFromFormat( 'H:i', $time );

        return $t && $t->format( 'H:i' ) === $time;
    }
}
