<?php
// phpcs:disable WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.InputNotValidated
// phpcs:disable WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
/**
 * Customer add-to-calendar links.
 *
 * @package YatoonBookingSystem
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class YATOON_Calendar_Links {
    public function __construct() {
        add_action( 'init', array( $this, 'maybe_download_ics' ) );
    }

    public static function get_calendar_url( $booking_id, $token ) {
        $booking_id = absint( $booking_id );
        $token      = sanitize_text_field( (string) $token );

        if ( ! $booking_id || '' === $token ) {
            return '';
        }

        return add_query_arg(
            array(
                'yatoon_ics' => '1',
                'booking_id' => $booking_id,
                'token'      => $token,
            ),
            home_url( '/' )
        );
    }

    public function maybe_download_ics() {
        if ( empty( $_GET['yatoon_ics'] ) ) {
            return;
        }

        $booking_id = isset( $_GET['booking_id'] ) ? absint( $_GET['booking_id'] ) : 0;
        $token      = isset( $_GET['token'] ) ? sanitize_text_field( wp_unslash( $_GET['token'] ) ) : '';

        if ( ! $booking_id || '' === $token ) {
            wp_die( esc_html__( 'Invalid calendar link.', 'yatoon-booking-system' ) );
        }

        global $wpdb;
        $bookings_table = $wpdb->prefix . 'yatoon_bookings';
        $services_table = $wpdb->prefix . 'yatoon_services';
        $staff_table    = $wpdb->prefix . 'yatoon_staff';

        // 4.6.2: this query matched on id + manage_token alone, with no expiry
        // check anywhere in the request -- unlike the cancel/reschedule paths in
        // class-yatoon-manage-booking.php, which have always honoured
        // manage_token_expires. A calendar link forwarded on, sitting in a proxy
        // log, or recovered from browser history therefore kept returning the
        // full booking row (customer name, phone, service, staff, time) forever.
        // Same rule as the manage endpoints: a NULL/empty expiry means "never
        // issued with a deadline" and stays valid, anything in the past does not.
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $booking = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM {$bookings_table}
                  WHERE id = %d
                    AND manage_token = %s
                    AND ( manage_token_expires IS NULL
                          OR manage_token_expires = ''
                          OR manage_token_expires >= %s )
                  LIMIT 1",
                $booking_id,
                $token,
                gmdate( 'Y-m-d H:i:s' )
            )
        );

        if ( ! $booking ) {
            wp_die( esc_html__( 'Calendar link expired or invalid.', 'yatoon-booking-system' ) );
        }

        $rows = array( $booking );
        if ( ! empty( $booking->group_booking_id ) ) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $group_rows = $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT * FROM {$bookings_table} WHERE group_booking_id = %s ORDER BY start_time ASC",
                    $booking->group_booking_id
                )
            );
            if ( ! empty( $group_rows ) ) {
                $rows = $group_rows;
            }
        }

        $service_names = array();
        foreach ( $rows as $row ) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $service_name = $wpdb->get_var(
                $wpdb->prepare(
                    "SELECT name FROM {$services_table} WHERE id = %d LIMIT 1",
                    absint( $row->service_id )
                )
            );
            if ( $service_name ) {
                $service_names[] = $service_name;
            }
        }
        $service_names = array_values( array_unique( array_filter( $service_names ) ) );

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $staff_name = $wpdb->get_var(
            $wpdb->prepare(
                "SELECT name FROM {$staff_table} WHERE id = %d LIMIT 1",
                absint( $booking->staff_id )
            )
        );

        $site_tz      = yatoon_timezone( isset( $booking->location_id ) ? $booking->location_id : null );
$site_tz_name = $site_tz->getName();
        $parse_local_datetime = static function ( $value ) use ( $site_tz ) {
            $dt = DateTimeImmutable::createFromFormat( 'Y-m-d H:i:s', (string) $value, $site_tz );
            if ( $dt instanceof DateTimeImmutable ) {
                return $dt;
            }
            return new DateTimeImmutable( (string) $value, $site_tz );
        };
        $start_times = array_map( static function ( $row ) use ( $parse_local_datetime ) {
            return $parse_local_datetime( $row->start_time );
        }, $rows );
        $end_times = array_map( static function ( $row ) use ( $parse_local_datetime ) {
            return $parse_local_datetime( $row->end_time );
        }, $rows );
        usort( $start_times, static function ( $a, $b ) {
            return $a->getTimestamp() <=> $b->getTimestamp();
        } );
        usort( $end_times, static function ( $a, $b ) {
            return $a->getTimestamp() <=> $b->getTimestamp();
        } );
        $event_start = reset( $start_times );
        $event_end   = end( $end_times );
        $utc_tz      = new DateTimeZone( 'UTC' );
        $event_start_utc = $event_start->setTimezone( $utc_tz );
        $event_end_utc   = $event_end->setTimezone( $utc_tz );

        $business_name  = get_option( 'yatoon_business_name', get_bloginfo( 'name' ) );
        $business_phone = get_option( 'yatoon_contact_phone', '' );
        $business_email = get_option( 'yatoon_contact_email', '' );
        $summary        = $business_name . ': ' . ( $service_names ? implode( ', ', $service_names ) : __( 'Appointment', 'yatoon-booking-system' ) );
        $description    = array_filter(
            array(
                $staff_name ? 'Staff: ' . $staff_name : '',
                $business_phone ? 'Phone: ' . $business_phone : '',
                $business_email ? 'Email: ' . $business_email : '',
                ! empty( $booking->notes ) ? 'Notes: ' . $booking->notes : '',
            )
        );

        $ics = array(
            'BEGIN:VCALENDAR',
            'VERSION:2.0',
            'PRODID:-//Yatoon//Booking System//EN',
            'CALSCALE:GREGORIAN',
            'METHOD:PUBLISH',
            'X-WR-TIMEZONE:' . $site_tz_name,
            'BEGIN:VEVENT',
            'UID:yatoon-booking-' . absint( $booking->id ) . '@' . wp_parse_url( home_url(), PHP_URL_HOST ),
            'DTSTAMP:' . gmdate( 'Ymd\THis\Z' ),
            'DTSTART:' . $event_start_utc->format( 'Ymd\THis\Z' ),
            'DTEND:' . $event_end_utc->format( 'Ymd\THis\Z' ),
            'SUMMARY:' . $this->escape_ics_text( $summary ),
            'DESCRIPTION:' . $this->escape_ics_text( implode( "\n", $description ) ),
            'END:VEVENT',
            'END:VCALENDAR',
        );

        nocache_headers();
        header( 'Content-Type: text/calendar; charset=utf-8; method=PUBLISH' );
        header( 'Content-Disposition: inline; filename="yatoon-appointment-' . absint( $booking->id ) . '.ics"' );
        header( 'Content-Transfer-Encoding: binary' );
        echo implode( "\r\n", $ics ) . "\r\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
        exit;
    }

    private function escape_ics_text( $text ) {
        $text = wp_strip_all_tags( (string) $text );
        $text = str_replace( array( '\\', ';', ',', "\r\n", "\n", "\r" ), array( '\\\\', '\;', '\,', '\n', '\n', '\n' ), $text );
        return $text;
    }
}
