<?php
/**
 * Event Creator
 *
 * Handles event creation with recurrence patterns and custom status
 *
 * @package NemtlyBooking
 */

namespace NemtlyBooking\Core;

use WP_Error;
use NemtlyBooking\Services\Event_Validator;

/**
 * Event Creator class
 *
 * @since PBOOKING_SINCE
 */
class Event_Creator {

    /**
     * Meta field constants
     */
    const META_EVENT_DATE         = 'nemtly_event_date';
    const META_EVENT_TIME         = 'nemtly_event_time';
    const META_EVENT_DATE_UTC     = 'nemtly_event_date_utc';
    const META_EVENT_TIME_UTC     = 'nemtly_event_time_utc';
    const META_EVENT_TIMEZONE     = 'nemtly_event_timezone';
    const META_EVENT_DURATION     = 'nemtly_event_duration';
    const META_LOCATION           = 'nemtly_location';
    const META_MAX_ATTENDEES      = 'nemtly_max_attendees';
    const META_BOOKING_CUTOFF     = 'nemtly_booking_cutoff';
    const META_ALLOW_WAITLIST     = 'nemtly_allow_waitlist';
    const META_RECURRENCE_PATTERN = 'nemtly_recurrence_pattern';
    const META_RECURRENCE_COUNT   = 'nemtly_recurrence_count';
    const META_RECURRENCE_END     = 'nemtly_recurrence_end';
    const META_PARENT_EVENT_ID    = 'nemtly_parent_event_id';
    const META_IS_RECURRING       = 'nemtly_is_recurring';
    const META_PRICE              = 'nemtly_price';
    const META_FEATURED_IMAGE     = '_thumbnail_id';
    const META_BUFFER_BEFORE      = '_nemtly_buffer_before';
    const META_BUFFER_AFTER       = '_nemtly_buffer_after';
    const META_EVENT_END_DATE     = 'nemtly_event_end_date';
    const META_EVENT_END_TIME     = 'nemtly_event_end_time';
    const META_EVENT_END_DATE_UTC = 'nemtly_event_end_date_utc';
    const META_EVENT_END_TIME_UTC = 'nemtly_event_end_time_utc';
    const META_EVENT_TYPE         = 'nemtly_event_type';
    const META_AVAILABILITY       = '_nemtly_availability';
    const META_STYLING            = 'nemtly_styling';



    /**
     * Create a new event
     *
     * @param array $event_data Event data array.
     *
     * @return int|WP_Error Post ID on success, WP_Error on failure.
     */
    public function create_event( $event_data ) {
        // DEBUG: trace the raw date fields received before validation/sanitization.
        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
            error_log(
                '[nemtly-debug] Event_Creator::create_event received → ' . wp_json_encode(
                    [
                        'event_type'     => $event_data['event_type'] ?? null,
                        'event_date'     => $event_data['event_date'] ?? null,
                        'event_time'     => $event_data['event_time'] ?? null,
                        'event_end_date' => $event_data['event_end_date'] ?? null,
                        'event_end_time' => $event_data['event_end_time'] ?? null,
                        'timezone'       => $event_data['timezone'] ?? null,
                    ]
                )
            );
        }

        // Validate using centralized validator
        $validation_result = Event_Validator::validate_event_data( $event_data );
        if ( \is_wp_error( $validation_result ) ) {
            return $validation_result;
        }
        // Sanitize data
        $sanitized_data = $this->sanitize_event_data( $event_data );
        // Create the main event post
        $post_id = $this->create_event_post( $sanitized_data );
        if ( \is_wp_error( $post_id ) ) {
            return $post_id;
        }
        // Save event meta data
        $this->save_event_meta( $post_id, $sanitized_data );

        // Handle featured image using WordPress native functions
        if ( ! empty( $sanitized_data['featured_image'] ) ) {
            $this->set_featured_image( $post_id, $sanitized_data['featured_image'] );
        }

        // Note: Recurrence is now handled via virtual instances, not by creating multiple posts
        // The recurrence pattern is stored in meta and calculated dynamically

        return $post_id;
    }



    /**
     * Sanitize event data
     *
     * @param array $event_data Raw event data.
     *
     * @return array Sanitized event data.
     */
    private function sanitize_event_data( $event_data ) {
        $event_date     = \sanitize_text_field( $event_data['event_date'] ?? '' );
        $event_time     = \sanitize_text_field( $event_data['event_time'] ?? '' );
        $event_end_date = \sanitize_text_field( $event_data['event_end_date'] ?? '' );
        $event_end_time = \sanitize_text_field( $event_data['event_end_time'] ?? '' );
        $timezone       = \sanitize_text_field( $event_data['timezone'] ?? \wp_timezone_string() );

        // Compute UTC date and time from local date/time + timezone, for both the
        // start and the end so consumers can compare against a single UTC baseline.
        $utc     = self::compute_utc( $event_date, $event_time, $timezone );
        $utc_end = self::compute_utc( $event_end_date, $event_end_time, $timezone );

        return [
            'title'              => \sanitize_text_field( $event_data['title'] ?? '' ),
            'description'        => \wp_kses_post( $event_data['description'] ?? '' ),
            'event_date'         => $event_date,
            'event_time'         => $event_time,
            'event_date_utc'     => $utc['date'],
            'event_time_utc'     => $utc['time'],
            'timezone'           => $timezone,
            'event_end_date'     => $event_end_date,
            'event_end_time'     => $event_end_time,
            'event_end_date_utc' => $utc_end['date'],
            'event_end_time_utc' => $utc_end['time'],
            'event_duration'     => \absint( $event_data['event_duration'] ?? 60 ),
            'location'           => \sanitize_text_field( $event_data['location'] ?? '' ),
            'max_attendees'      => \absint( $event_data['max_attendees'] ?? 0 ),
            'booking_cutoff'     => \absint( $event_data['booking_cutoff'] ?? 0 ),
            'allow_waitlist'     => (bool) ( $event_data['allow_waitlist'] ?? false ),
            'recurrence_pattern' => \sanitize_text_field( $event_data['recurrence_pattern'] ?? 'none' ),
            'recurrence_count'   => \absint( $event_data['recurrence_count'] ?? 1 ),
            'recurrence_end'     => \sanitize_text_field( $event_data['recurrence_end'] ?? '' ),
            'is_recurring'       => (bool) ( $event_data['is_recurring'] ?? false ),
            'post_status'        => \sanitize_text_field( $event_data['post_status'] ?? 'draft' ),
            // featured_image can be an attachment ID, external URL or base64 data URL.
            // Don't run sanitize_url() here because data URLs will be stripped — validation/handling
            // is performed in the image handling methods.
            'featured_image'     => $event_data['featured_image'] ?? '',
            'price'              => isset( $event_data['price'] ) ? (float) $event_data['price'] : 0,
            'buffer_before'      => \absint( $event_data['buffer_before'] ?? 0 ),
            'buffer_after'       => \absint( $event_data['buffer_after'] ?? 0 ),
            'event_type'         => \sanitize_text_field( $event_data['event_type'] ?? 'one-on-one' ),
            'availability'       => $this->resolve_availability( $event_data ),
            'styling'            => self::sanitize_styling( $event_data['styling'] ?? [] ),
        ];
    }

    /**
     * Sanitize per-event styling tokens.
     *
     * All 13 tokens (6 colors + 7 enums) are validated against the schema
     * defined in Event_Templates. The optional `template` key records which
     * template was selected (for UI display); token values are resolved
     * client-side when the admin picks a template, so by the time data
     * reaches here they are explicit hex/enum values. An empty string means
     * "inherit the CSS default" — only non-empty values produce an override.
     *
     * @since PBOOKING_SINCE
     *
     * @param array $styling Raw styling array from the request.
     *
     * @return array Sanitized styling tokens keyed by token name, plus a `template` key.
     */
    public static function sanitize_styling( $styling ) {
        $styling   = is_array( $styling ) ? $styling : [];
        $sanitized = [];

        // Record the selected template id for UI reference.
        $template              = \sanitize_key( $styling['template'] ?? '' );
        $sanitized['template'] = Event_Templates::exists( $template ) ? $template : '';

        // Color tokens — sanitize_hex_color, '' means inherit CSS default.
        foreach ( Event_Templates::COLOR_KEYS as $key ) {
            $value = $styling[ $key ] ?? '';

            if ( '' === $value || null === $value ) {
                $sanitized[ $key ] = '';
                continue;
            }

            $hex               = \sanitize_hex_color( $value );
            $sanitized[ $key ] = $hex ? $hex : '';
        }

        // Enum tokens — validate against the allowed values in the registry.
        foreach ( Event_Templates::ENUM_VALUES as $key => $allowed ) {
            $value             = \sanitize_key( $styling[ $key ] ?? '' );
            $sanitized[ $key ] = in_array( $value, $allowed, true ) ? $value : '';
        }

        return $sanitized;
    }

    /**
     * Create the main event post
     *
     * @param array $event_data Sanitized event data.
     *
     * @return int|WP_Error Post ID on success, WP_Error on failure.
     */
    private function create_event_post( $event_data ) {
        $post_args = [
            'post_title'   => $event_data['title'],
            'post_content' => $event_data['description'],
            'post_type'    => 'nemtly_event',
            'post_status'  => $event_data['post_status'],
            'post_author'  => get_current_user_id(),
        ];
        $post_id   = wp_insert_post( $post_args );
        if ( is_wp_error( $post_id ) ) {
            return $post_id;
        }
        if ( 0 === $post_id ) {
            return new \WP_Error( 'event_creation_failed', __( 'Failed to create event.', 'nemtly-booking' ) );
        }

        return $post_id;
    }

    /**
     * Save event meta data
     *
     * @param int   $post_id    Post ID.
     * @param array $event_data Event data.
     */
    private function save_event_meta( $post_id, $event_data ) {
        $meta_fields = [
            self::META_EVENT_DATE         => $event_data['event_date'],
            self::META_EVENT_TIME         => $event_data['event_time'],
            self::META_EVENT_DATE_UTC     => $event_data['event_date_utc'],
            self::META_EVENT_TIME_UTC     => $event_data['event_time_utc'],
            self::META_EVENT_TIMEZONE     => $event_data['timezone'],
            self::META_EVENT_END_DATE     => $event_data['event_end_date'],
            self::META_EVENT_END_TIME     => $event_data['event_end_time'],
            self::META_EVENT_END_DATE_UTC => $event_data['event_end_date_utc'],
            self::META_EVENT_END_TIME_UTC => $event_data['event_end_time_utc'],
            self::META_EVENT_DURATION     => $event_data['event_duration'],
            self::META_LOCATION           => $event_data['location'],
            self::META_MAX_ATTENDEES      => $event_data['max_attendees'],
            self::META_BOOKING_CUTOFF     => $event_data['booking_cutoff'],
            self::META_ALLOW_WAITLIST     => $event_data['allow_waitlist'],
            self::META_RECURRENCE_PATTERN => $event_data['recurrence_pattern'],
            self::META_RECURRENCE_COUNT   => $event_data['recurrence_count'],
            self::META_RECURRENCE_END     => $event_data['recurrence_end'],
            self::META_IS_RECURRING       => $event_data['is_recurring'],
            self::META_PRICE              => $event_data['price'],
            self::META_BUFFER_BEFORE      => $event_data['buffer_before'],
            self::META_BUFFER_AFTER       => $event_data['buffer_after'],
            self::META_EVENT_TYPE         => $event_data['event_type'] ?? 'one-on-one',
            self::META_AVAILABILITY       => wp_json_encode( $event_data['availability'] ?? [] ),
            self::META_STYLING            => wp_json_encode( $event_data['styling'] ?? [] ),
        ];

        foreach ( $meta_fields as $meta_key => $meta_value ) {
            update_post_meta( $post_id, $meta_key, $meta_value );
        }

        // DEBUG: trace the date-related meta actually written to the DB.
        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
            error_log(
                '[nemtly-debug] Event_Creator::save_event_meta wrote → ' . wp_json_encode(
                    [
                        'post_id'                     => $post_id,
                        self::META_EVENT_DATE         => $meta_fields[ self::META_EVENT_DATE ],
                        self::META_EVENT_TIME         => $meta_fields[ self::META_EVENT_TIME ],
                        self::META_EVENT_DATE_UTC     => $meta_fields[ self::META_EVENT_DATE_UTC ],
                        self::META_EVENT_TIME_UTC     => $meta_fields[ self::META_EVENT_TIME_UTC ],
                        self::META_EVENT_END_DATE     => $meta_fields[ self::META_EVENT_END_DATE ],
                        self::META_EVENT_END_TIME     => $meta_fields[ self::META_EVENT_END_TIME ],
                        self::META_EVENT_END_DATE_UTC => $meta_fields[ self::META_EVENT_END_DATE_UTC ],
                        self::META_EVENT_END_TIME_UTC => $meta_fields[ self::META_EVENT_END_TIME_UTC ],
                        self::META_EVENT_TIMEZONE     => $meta_fields[ self::META_EVENT_TIMEZONE ],
                    ]
                )
            );

        }
    }

    /**
     * Create recurrence rule for master event
     *
     * @since PBOOKING_SINCE
     *
     * @param array $event_data Event data with recurrence settings.
     *
     * @return string Recurrence rule string for storage.
     */
    private function create_recurrence_rule( $event_data ) {
        if ( empty( $event_data['recurrence_pattern'] ) || 'none' === $event_data['recurrence_pattern'] ) {
            return '';
        }

        // Simple pattern conversion - can be enhanced later
        return strtoupper( $event_data['recurrence_pattern'] );
    }

    /**
     * Combine date and time for WordPress post_date
     *
     * @param string $date Date in Y-m-d format.
     * @param string $time Time in H:i format.
     *
     * @return string Combined datetime in Y-m-d H:i:s format.
     */
    private function combine_date_time( $date, $time ) {
        return $date . ' ' . $time . ':00';
    }

    /**
     * Get supported recurrence patterns
     *
     * @return array Recurrence patterns.
     */
    public static function get_recurrence_patterns() {
        return Event_Validator::get_recurrence_patterns();
    }

    /**
     * Resolve availability for a new event.
     *
     * If explicit availability is provided, use it. For slot-based events
     * (one-on-one, group) with no availability, fall back to the global
     * business hours setting, then to the hardcoded default.
     *
     * @since PBOOKING_SINCE
     *
     * @param array $event_data Raw event data.
     *
     * @return array Availability array.
     */
    private function resolve_availability( $event_data ) {
        if ( ! empty( $event_data['availability'] ) ) {
            return $event_data['availability'];
        }

        $event_type = $event_data['event_type'] ?? 'one-on-one';

        if ( ! in_array( $event_type, [ 'one-on-one', 'group' ], true ) ) {
            return [];
        }

        $business_hours = \get_option( 'nemtly_booking_settings', [] );
        $hours          = $business_hours['business_hours'] ?? [];

        if ( ! empty( $hours ) ) {
            return $hours;
        }

        // Hardcoded fallback: Mon-Fri 09:00-17:00.
        return [
            'mon' => [
				[
					'start' => '09:00',
					'end'   => '17:00',
				],
			],
            'tue' => [
				[
					'start' => '09:00',
					'end'   => '17:00',
				],
			],
            'wed' => [
				[
					'start' => '09:00',
					'end'   => '17:00',
				],
			],
            'thu' => [
				[
					'start' => '09:00',
					'end'   => '17:00',
				],
			],
            'fri' => [
				[
					'start' => '09:00',
					'end'   => '17:00',
				],
			],
            'sat' => [],
            'sun' => [],
        ];
    }

    /**
     * Compute UTC date and time from local date/time and timezone
     *
     * @since PBOOKING_SINCE
     *
     * @param string $date     Local date in Y-m-d format.
     * @param string $time     Local time in H:i format.
     * @param string $timezone IANA timezone string (e.g., America/New_York).
     *
     * @return array{date: string, time: string} UTC date and time.
     */
    public static function compute_utc( $date, $time, $timezone ) {
        if ( empty( $date ) || empty( $time ) ) {
            return [
				'date' => '',
				'time' => '',
			];
        }

        try {
            $local_tz = new \DateTimeZone( $timezone );
            $utc_tz   = new \DateTimeZone( 'UTC' );
            $dt       = new \DateTime( $date . ' ' . $time, $local_tz );
            $dt->setTimezone( $utc_tz );

            return [
                'date' => $dt->format( 'Y-m-d' ),
                'time' => $dt->format( 'H:i' ),
            ];
        } catch ( \Exception $e ) {
            // Fallback: treat as UTC if timezone is invalid
            return [
                'date' => $date,
                'time' => $time,
            ];
        }
    }

    /**
     * Set featured image for an event using WordPress native functions
     *
     * @param int    $post_id       Post 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_featured_image( $post_id, $image_url_or_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
            // For now, we'll assume it's a base64 data URL or external URL
            // In a production environment, you'd want to download and attach the image
            $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( $post_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( $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( $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;
    }
}
