<?php
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Service for handling dates, times, timezones, and scheduling validation.
 */
class EEP_Date_Helper {

    /**
     * Normalizes a datetime-local input string.
     */
    public function normalize_datetime_local($value) {
        $value = sanitize_text_field((string) $value);
        if ($value === '') {
            return '';
        }
        return str_replace('T', ' ', $value);
    }

    /**
     * Checks if a datetime string includes a specific time.
     */
    public function event_datetime_has_time($value) {
        $value = $this->normalize_datetime_local($value);
        if ($value === '') {
            return false;
        }
        return preg_match('/\b\d{1,2}:\d{2}(:\d{2})?\b/', $value) === 1;
    }

    /**
     * Builds a unified datetime string from separate date and time inputs.
     */
    public function build_event_datetime_from_inputs($date_value, $time_value = '') {
        $date_value = sanitize_text_field((string) $date_value);
        $time_value = sanitize_text_field((string) $time_value);
        
        if ($date_value === '') {
            return '';
        }

        $normalized_date = str_replace('/', '-', trim((string) $date_value));
        
        if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $normalized_date) !== 1) {
            // Backward compatibility for legacy datetime-local payloads.
            return $this->normalize_datetime_local($date_value);
        }

        if ($time_value !== '' && preg_match('/^(\d{1,2}):(\d{2})$/', $time_value, $matches)) {
            $hour = min(23, max(0, (int) ($matches[1] ?? 0)));
            $minute = min(59, max(0, (int) ($matches[2] ?? 0)));
            return sprintf('%s %02d:%02d', $normalized_date, $hour, $minute);
        }

        return $normalized_date;
    }

    /**
     * Formats a datetime string for frontend display using WP settings.
     */
    public function format_event_datetime_for_display($value) {
        $normalized = $this->normalize_datetime_local($value);
        if ($normalized === '') {
            return '';
        }
        
        $ts = strtotime($normalized);
        if (!$ts) {
            return '';
        }
        
        $format = get_option('date_format');
        if ($this->event_datetime_has_time($normalized)) {
            $format .= ' ' . get_option('time_format');
        }
        
        return wp_date($format, $ts);
    }

    /**
     * Formats a datetime string for use in an HTML <input type="datetime-local">
     */
    public function format_datetime_local_input($value) {
        $value = sanitize_text_field((string) $value);
        if ($value === '') {
            return '';
        }
        
        $value = str_replace('T', ' ', $value);
        $dt = null;
        
        try {
            $dt = new DateTime($value, wp_timezone());
        } catch (Exception $e) {
            $dt = null;
        }
        
        if (!$dt instanceof DateTime) {
            return '';
        }
        
        return $dt->format('Y-m-d\TH:i');
    }

    /**
     * Retrieves a cached list of valid timezone identifiers.
     */
    public function get_timezone_identifiers() {
        static $identifiers = null;
        if ($identifiers === null) {
            $identifiers = timezone_identifiers_list();
        }
        return $identifiers;
    }

    /**
     * Sanitizes a timezone string against PHP's valid identifiers.
     */
    public function sanitize_timezone_value($value) {
        $value = sanitize_text_field((string) $value);
        if ($value === '') {
            return '';
        }
        return in_array($value, $this->get_timezone_identifiers(), true) ? $value : '';
    }

    /**
     * Renders an HTML select dropdown for timezones.
     */
    public function render_timezone_dropdown($name, $selected = '') {
        $selected = $this->sanitize_timezone_value($selected);
        ob_start();
        ?>
        <select name="<?php echo esc_attr($name); ?>">
            <option value=""><?php esc_html_e('Select Timezone', 'nexdirectory'); ?></option>
            <?php foreach ($this->get_timezone_identifiers() as $tz) : ?>
                <option value="<?php echo esc_attr($tz); ?>" <?php selected($selected, $tz); ?>><?php echo esc_html($tz); ?></option>
            <?php endforeach; ?>
        </select>
        <?php
        return ob_get_clean();
    }

    /**
     * Validates start and end times logically.
     */
    public function validate_event_times($start, $end, $allow_past = true) {
        $start = $this->normalize_datetime_local($start);
        $end = $this->normalize_datetime_local($end);
        
        if ($start === '') {
            return new WP_Error('eep_missing_start', __('Start date is required.', 'nexdirectory'));
        }

        $start_ts = strtotime($start);
        if (!$start_ts) {
            return new WP_Error('eep_invalid_start', __('Start date is invalid.', 'nexdirectory'));
        }
        if (!$allow_past && $start_ts < (time() - 60)) {
            return new WP_Error('eep_start_in_past', __('Start date cannot be in the past.', 'nexdirectory'));
        }

        if ($end !== '') {
            $end_ts = strtotime($end);
            if (!$end_ts) {
                return new WP_Error('eep_invalid_end', __('End date is invalid.', 'nexdirectory'));
            }
            
            // Cleaned up logic: Combine the basic "end before start" check with the "exact same time" check
            if (
                $end_ts < $start_ts || 
                ($end_ts === $start_ts && $this->event_datetime_has_time($start) && $this->event_datetime_has_time($end))
            ) {
                return new WP_Error('eep_end_before_start', __('End date/time must be after the start date/time.', 'nexdirectory'));
            }
        }

        return true;
    }

    /**
     * Formats a post's event dates into a single human-readable string.
     */
    public function format_event_date($post_id) {
        $start = get_post_meta($post_id, 'eep_start', true);
        $end = get_post_meta($post_id, 'eep_end', true);
        $timezone = $this->sanitize_timezone_value((string) get_post_meta($post_id, 'eep_timezone', true));

        if (!$start) {
            return __('Date TBD', 'nexdirectory');
        }

        $tz = null;
        if ($timezone !== '') {
            try {
                $tz = new DateTimeZone($timezone);
            } catch (Exception $e) {
                $tz = null;
            }
        }
        
        $start_dt = null;
        if ($tz) {
            $start_dt = DateTime::createFromFormat('Y-m-d H:i', (string) $start, $tz);
        }
        
        if (!$start_dt) {
            $start_ts = strtotime((string) $start);
            if (!$start_ts) {
                return __('Date TBD', 'nexdirectory');
            }
            $start_dt = new DateTime('@' . $start_ts);
            $start_dt->setTimezone($tz ?: wp_timezone());
        }

        if (!$start_dt instanceof DateTime) {
            return __('Date TBD', 'nexdirectory');
        }

        $start_has_time = $this->event_datetime_has_time((string) $start);
        $end_has_time = $this->event_datetime_has_time((string) $end);
        $start_format = get_option('date_format') . ($start_has_time ? ' ' . get_option('time_format') : '');
        $formatted = $start_dt->format($start_format);

        if ($end) {
            $end_dt = null;
            if ($tz) {
                $end_dt = DateTime::createFromFormat('Y-m-d H:i', (string) $end, $tz);
            }
            if (!$end_dt) {
                $end_ts = strtotime((string) $end);
                if ($end_ts) {
                    $end_dt = new DateTime('@' . $end_ts);
                    $end_dt->setTimezone($tz ?: wp_timezone());
                }
            }
            if ($end_dt instanceof DateTime) {
                $same_day = $start_dt->format('Y-m-d') === $end_dt->format('Y-m-d');
                if ($same_day && $start_has_time && $end_has_time) {
                    $formatted .= ' - ' . $end_dt->format(get_option('time_format'));
                } else {
                    $end_format = get_option('date_format') . ($end_has_time ? ' ' . get_option('time_format') : '');
                    $end_label = $end_dt->format($end_format);
                    if ($end_label !== $formatted) {
                        $formatted .= ' - ' . $end_label;
                    }
                }
            }
        }

        if ($timezone !== '') {
            $formatted .= ' (' . $timezone . ')';
        }

        return $formatted;
    }
}