<?php
namespace TCB\Frontend;
use TCB\DB\{PartnersDAO, SlotsDAO, BookingsDAO};
use TCB\Admin\EmailEngine;
if ( ! defined( 'ABSPATH' ) ) { exit; }

class BookingPage {

    private static function event_context(): array {
        $name = 'TechCon SoCal 2026';
        $date_range = 'May 21-23, 2026';
        if ( class_exists( '\TCA\DB\EventsDAO' ) ) {
            $events = \TCA\DB\EventsDAO::get_all( 'live' );
            if ( empty( $events ) ) { $events = \TCA\DB\EventsDAO::get_all(); }
            if ( ! empty( $events[0] ) ) {
                $name = trim( (string) ( $events[0]['name'] ?? $name ) ) ?: $name;
                $date_range = trim( (string) ( $events[0]['date_range'] ?? $date_range ) ) ?: $date_range;
            }
        }
        $year = '2026';
        if ( preg_match( '/(20\d{2})/', $name . ' ' . $date_range, $m ) ) { $year = $m[1]; }
        return [ 'name' => $name, 'date_range' => $date_range, 'year' => $year, 'media_label' => $name . ' - Media Interviews' ];
    }


    public static function init(): void {
        add_action( 'init', [ self::class, 'add_rewrite_rules' ] );
        add_filter( 'query_vars', [ self::class, 'query_vars' ] );
        // Render partner booking pages early so theme redirects or missing rewrites cannot send users back to dashboards.
        add_action( 'template_redirect', [ self::class, 'maybe_render' ], 0 );
        // AJAX booking submission requires a logged-in speaker.
        add_action( 'wp_ajax_tcb_submit_booking', [ self::class, 'handle_submission' ] );
        add_action( 'wp_ajax_nopriv_tcb_submit_booking', [ self::class, 'handle_login_required' ] );
    }

    public static function add_rewrite_rules(): void {
        add_rewrite_rule( '^book/([a-z0-9-]+)/?$', 'index.php?tcb_partner_slug=$matches[1]', 'top' );
    }

    public static function query_vars( array $vars ): array {
        $vars[] = 'tcb_partner_slug';
        return $vars;
    }


    private static function current_path(): string {
        $path = trim( (string) parse_url( $_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH ), '/' );
        $home_path = trim( (string) parse_url( home_url( '/' ), PHP_URL_PATH ), '/' );
        if ( $home_path && strpos( $path, $home_path . '/' ) === 0 ) {
            $path = substr( $path, strlen( $home_path ) + 1 );
        }
        return trim( $path, '/' );
    }

    public static function maybe_render(): void {
        $slug = get_query_var( 'tcb_partner_slug' );
        if ( ! $slug ) {
            $path = self::current_path();
            if ( preg_match( '#^book/([^/]+)/?$#i', $path, $m ) ) { $slug = sanitize_title( $m[1] ); }
        }
        if ( ! $slug ) return;

        $partner = PartnersDAO::get_by_slug( $slug );
        if ( ! $partner ) {
            wp_die( esc_html__( 'Media partner not found.', 'techcon-booking' ), 404 );
        }

        self::render_booking_page( $partner );
        exit;
    }

    public static function render_shortcode( array $atts ): string {
        $atts    = shortcode_atts( [ 'partner' => '' ], $atts );
        $partner = PartnersDAO::get_by_slug( sanitize_title( $atts['partner'] ) );
        if ( ! $partner ) return '<p>Partner not found.</p>';
        ob_start();
        self::render_booking_page( $partner, true );
        return ob_get_clean();
    }

    private static function render_booking_page( array $partner, bool $inline = false ): void {
        // Enqueue assets.
        wp_enqueue_style(  'tcb-booking', TCB_PLUGIN_URL . 'assets/frontend/booking.css', [], TCB_VERSION );
        $user = wp_get_current_user();
        $is_speaker = $user->ID && in_array( 'tc_speaker', (array) $user->roles, true );
        $speaker_profile = self::current_speaker_payload();
        wp_enqueue_script( 'tcb-booking', TCB_PLUGIN_URL . 'assets/frontend/booking.js', [], TCB_VERSION, true );
        $event_ctx = self::event_context();
        wp_localize_script( 'tcb-booking', 'tcbConfig', [
            'ajaxUrl'   => admin_url( 'admin-ajax.php' ),
            'nonce'     => wp_create_nonce( 'tcb-booking-nonce' ),
            'eventLabel' => $event_ctx['media_label'],
            'eventName' => $event_ctx['name'],
            'eventYear' => $event_ctx['year'],
            'bookingMode' => get_option( 'tcb_booking_mode', 'simple' ),
            'partnerId' => (int) $partner['id'],
            'slots'     => self::build_slots_payload( (int) $partner['id'] ),
            'myRequests' => self::build_my_requests_payload( (int) $partner['id'] ),
            'loggedIn' => (bool) $user->ID,
            'isSpeaker' => (bool) $is_speaker,
            'loginUrl' => home_url( '/speaker-login/' ),
            'dashboardUrl' => home_url( '/speaker-dashboard/' ),
            'bookingIndexUrl' => home_url( '/booking/' ),
            'speaker' => $speaker_profile,
            'partner'   => [
                'name'    => $partner['name'],
                'logo'    => $partner['logo_url'],
                'color'   => $partner['color'],
                'bio'     => $partner['bio'],
                'open'    => (bool) $partner['booking_open'],
            ],
        ] );

        if ( ! $inline ) {
            // Full page render.
            $title = 'Book with ' . $partner['name'] . ' — ' . $event_ctx['name'];
            ?><!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?php echo esc_html( $title ); ?></title>
<?php wp_head(); ?>
</head>
<body class="tcb-booking-body">
<?php echo class_exists( '\\TCB\\Frontend\\SystemNavigation' ) ? \TCB\Frontend\SystemNavigation::render() : ''; ?>
<div id="tcb-booking-root"></div>
<?php wp_footer(); ?>
</body></html>
<?php
        } else {
            echo '<div id="tcb-booking-root"></div>';
        }
    }

    private static function build_slots_payload( int $partner_id ): array {
        $slots = SlotsDAO::get_by_partner( $partner_id );
        $speaker_id = self::current_speaker_id();
        $simple_mode = get_option( 'tcb_booking_mode', 'simple' ) === 'simple';
        $requests_by_slot = [];
        if ( $speaker_id ) {
            foreach ( BookingsDAO::get_request_history_by_speaker( $speaker_id ) as $r ) {
                if ( (int) ( $r['partner_id'] ?? 0 ) !== $partner_id ) continue;
                $requests_by_slot[ (int) ( $r['id'] ?? 0 ) ] = $r;
            }
        }
        $day_locks = [];
        $by_day = [];
        foreach ( $slots as $s ) {
            $day = (string) $s['event_day'];
            if ( ! isset( $day_locks[ $day ] ) ) {
                $day_locks[ $day ] = $speaker_id ? BookingsDAO::speaker_partner_day_lock_reason( $partner_id, $day, $speaker_id ) : [ 'locked' => false ];
            }
            $raw_status = $s['status'] ?? 'available';
            $status = $raw_status;
            $slot_id = (int) $s['id'];
            $disabled = $status !== 'available';
            $status_label = self::slot_status_label( $status );
            $lock_message = '';

            // In Simple Mode, a requested slot is held for that speaker until the
            // partner accepts or rejects. If rejected, the exact slot is blocked
            // only for that rejected speaker and remains available to others.

            $mine = $requests_by_slot[ $slot_id ] ?? null;
            // If the request-history table is missing/stale but the live slot
            // still belongs to the current speaker, treat it as this speaker's
            // own booking. This prevents "Booked" from looking like someone
            // else booked it and keeps the main dashboard in sync.
            if ( ! $mine && $speaker_id && (int) ( $s['speaker_id'] ?? 0 ) === $speaker_id && in_array( (string) $status, [ 'pending', 'approved' ], true ) ) {
                $mine = [
                    'status' => (string) $status,
                    'allow_rebook' => 0,
                    'id' => $slot_id,
                    'partner_id' => $partner_id,
                    'event_day' => $day,
                    'start_time' => $s['start_time'] ?? '',
                    'end_time' => $s['end_time'] ?? '',
                ];
            }
            if ( $mine ) {
                $mine_status = (string) ( $mine['status'] ?? '' );
                if ( $mine_status === 'declined' ) {
                    $status = ! empty( $mine['allow_rebook'] ) ? 'declined_rebook' : 'declined_blocked';
                    $disabled = true;
                    $status_label = ! empty( $mine['allow_rebook'] ) ? 'Declined — choose another available slot' : 'Declined — rebooking not allowed';
                    $lock_message = ! empty( $mine['allow_rebook'] ) ? 'This request was declined, but this partner has allowed you to choose another available slot for this day.' : 'This request was declined. You are not allowed to book another slot with this partner on this day unless the partner reopens it.';
                } elseif ( $mine_status === 'pending' ) {
                    $status = 'pending_self';
                    $disabled = true;
                    $status_label = $simple_mode ? 'Request Sent' : 'Reserved by you — pending review';
                    $lock_message = $simple_mode ? 'Your request has been sent to this media partner for this date. The partner can accept or reject it from their dashboard.' : 'You already have a pending request with this partner on this day.';
                } elseif ( $mine_status === 'approved' ) {
                    $status = 'approved_self';
                    $disabled = true;
                    $status_label = $simple_mode ? 'Booked' : 'Booked by you';
                    $lock_message = $simple_mode ? 'Your interview request has been confirmed by this media partner.' : 'You already have a booked interview with this partner on this day.';
                } elseif ( $mine_status === 'cancelled' ) {
                    $status = 'cancelled_self';
                    $disabled = true;
                    $status_label = 'Withdrawn';
                }
            } elseif ( $simple_mode && in_array( (string) ( $s['status'] ?? 'available' ), [ 'pending', 'approved' ], true ) ) {
                $disabled = true;
                $status = ( (string) ( $s['status'] ?? '' ) === 'approved' ) ? 'approved_taken' : 'pending_taken';
                $status_label = ( (string) ( $s['status'] ?? '' ) === 'approved' ) ? 'Unavailable' : 'Already requested';
                $lock_message = 'This time slot is not available because another speaker has already requested it.';
            } elseif ( ! empty( $day_locks[ $day ]['locked'] ) && ( $simple_mode || ( $s['status'] ?? 'available' ) === 'available' ) ) {
                $reason = (string) ( $day_locks[ $day ]['reason'] ?? 'day_locked' );
                $status = 'day_locked_' . $reason;
                $disabled = true;
                $status_label = $simple_mode ? 'Request already sent today' : self::day_lock_label( $reason, $day_locks[ $day ] );
                $lock_message = (string) ( $day_locks[ $day ]['message'] ?? 'You already have a request with this partner on this day.' );
            } elseif ( ! $simple_mode && $speaker_id && ( $s['status'] ?? 'available' ) === 'available' ) {
                $time_lock = BookingsDAO::speaker_time_lock_reason(
                    $day,
                    (string) ( $s['start_time'] ?? $s['time_start'] ?? '' ),
                    (string) ( $s['end_time'] ?? $s['time_end'] ?? '' ),
                    $speaker_id,
                    $slot_id
                );
                if ( ! empty( $time_lock['locked'] ) ) {
                    $reason = (string) ( $time_lock['reason'] ?? 'time_conflict' );
                    $status = 'time_locked_' . $reason;
                    $disabled = true;
                    $status_label = self::time_lock_label( $time_lock );
                    $lock_message = (string) ( $time_lock['message'] ?? 'You already have another interview at this exact time.' );
                }
            }

            if ( ! isset( $by_day[ $day ] ) ) $by_day[ $day ] = [];
            $by_day[ $day ][] = [
                'id'    => $slot_id,
                'start' => $s['start_time'],
                'end'   => $s['end_time'],
                'label' => date( 'g:i A', strtotime( $s['start_time'] ) ) . ' – ' . date( 'g:i A', strtotime( $s['end_time'] ) ),
                'status' => $status,
                'disabled' => $disabled,
                'statusLabel' => $status_label,
                'lockMessage' => $lock_message,
                'date' => $s['event_day'],
            ];
        }
        ksort( $by_day );
        $out = [];
        foreach ( $by_day as $date => $slots_arr ) {
            $out[] = [ 'date' => $date, 'label' => date( 'l, F j', strtotime( $date ) ), 'slots' => $slots_arr ];
        }
        return $out;
    }

    private static function build_my_requests_payload( int $partner_id ): array {
        $speaker_id = self::current_speaker_id();
        if ( ! $speaker_id ) return [];
        $out = [];
        foreach ( BookingsDAO::get_request_history_by_speaker( $speaker_id ) as $r ) {
            if ( (int) ( $r['partner_id'] ?? 0 ) !== $partner_id ) continue;
            $status = (string) ( $r['status'] ?? '' );
            $out[] = [
                'slot_id' => (int) ( $r['slot_id'] ?? $r['id'] ?? 0 ),
                'date' => (string) ( $r['event_day'] ?? '' ),
                'dateLabel' => ! empty( $r['event_day'] ) ? date( 'l, F j', strtotime( $r['event_day'] ) ) : '',
                'time' => date( 'g:i A', strtotime( $r['start_time'] ?? '' ) ) . ' – ' . date( 'g:i A', strtotime( $r['end_time'] ?? '' ) ),
                'status' => $status,
                'statusLabel' => self::slot_status_label( $status ),
                'allowRebook' => (int) ( $r['allow_rebook'] ?? 0 ),
                'message' => self::request_message( $status, (int) ( $r['allow_rebook'] ?? 0 ) ),
            ];
        }
        return $out;
    }

    private static function current_speaker_id(): int {
        if ( ! is_user_logged_in() || ! class_exists( '\TCA\Installer' ) ) return 0;
        global $wpdb;
        return (int) $wpdb->get_var( $wpdb->prepare( 'SELECT id FROM ' . \TCA\Installer::t_speakers() . ' WHERE wp_user_id=%d', get_current_user_id() ) );
    }

    private static function slot_status_label( string $status ): string {
        if ( get_option( 'tcb_booking_mode', 'simple' ) === 'simple' ) {
            switch ( $status ) {
                case 'pending':
                case 'pending_self': return 'Request Sent';
                case 'approved':
                case 'approved_self': return 'Booked';
                case 'declined':
                case 'declined_rebook':
                case 'declined_blocked': return 'Rejected';
                case 'reserved':
                case 'disabled': return 'Unavailable';
                case 'cancelled':
                case 'cancelled_self': return 'Request Removed';
                default: return 'Available';
            }
        }
        switch ( $status ) {
            case 'pending': return 'Reserved';
            case 'approved': return 'Booked';
            case 'reserved': return 'Reserved';
            case 'disabled': return 'Unavailable';
            case 'declined': return 'Declined';
            case 'declined_blocked': return 'Declined — rebooking not allowed';
            case 'declined_rebook': return 'Declined — choose another slot';
            case 'pending_self': return 'Reserved by you';
            case 'approved_self': return 'Booked by you';
            case 'cancelled': return 'Withdrawn';
            case 'cancelled_self': return 'Withdrawn';
            case 'day_locked': return 'Already requested this partner today';
            default: return 'Available';
        }
    }

    private static function day_lock_label( string $reason, array $context = [] ): string {
        switch ( $reason ) {
            case 'approved': return 'Already booked with this partner today';
            case 'pending': return 'Already reserved with this partner today';
            case 'declined_blocked': return 'Rebooking not allowed for this partner/day';
            default: return 'Already requested this partner today';
        }
    }

    private static function time_lock_label( array $context = [] ): string {
        $partner = trim( (string) ( $context['partner_name'] ?? '' ) );
        if ( $partner ) {
            return 'Time conflict: ' . $partner;
        }
        return 'Time conflict';
    }

    private static function request_message( string $status, int $allow_rebook = 0 ): string {
        if ( get_option( 'tcb_booking_mode', 'simple' ) === 'simple' ) {
            if ( $status === 'approved' ) return 'Your interview request has been confirmed by this media partner.';
            if ( $status === 'pending' ) return 'Your request has been sent to this media partner.';
            if ( $status === 'cancelled' ) return 'Request removed.';
            return '';
        }
        if ( $status === 'pending' ) return 'Reserved/pending: your request is waiting for an update.';
        if ( $status === 'approved' ) return 'Booked: your interview is confirmed.';
        if ( $status === 'declined' && $allow_rebook ) return 'Declined: this partner has allowed you to choose another available slot for this day.';
        if ( $status === 'declined' ) return 'Declined: you are not allowed to book another slot with this partner on this day unless the partner reopens it.';
        if ( $status === 'cancelled' ) return 'Withdrawn: this request was cancelled.';
        return '';
    }

    private static function current_speaker_payload(): array {
        if ( ! is_user_logged_in() || ! class_exists( '\TCA\DB\SpeakersDAO' ) ) return [];
        global $wpdb;
        $speaker_id = (int) $wpdb->get_var( $wpdb->prepare( 'SELECT id FROM ' . \TCA\Installer::t_speakers() . ' WHERE wp_user_id=%d', get_current_user_id() ) );
        if ( ! $speaker_id ) return [];
        $sp = \TCA\DB\SpeakersDAO::get_by_id( $speaker_id );
        if ( ! $sp ) return [];
        return [
            'id' => (int) $sp['id'],
            'name' => $sp['name'] ?? '',
            'email' => $sp['email'] ?? '',
            'title' => $sp['position'] ?? '',
            'company' => $sp['company'] ?? '',
        ];
    }

    public static function handle_login_required(): void {
        wp_send_json_error( [ 'message' => 'Please log in as a TechCon speaker before requesting a booking.', 'login' => home_url( '/speaker-login/' ) ], 403 );
    }

    // ── AJAX booking submission ───────────────────────────────────────────────
    public static function handle_submission(): void {
        if ( ! check_ajax_referer( 'tcb-booking-nonce', 'nonce', false ) ) {
            wp_send_json_error( [ 'message' => 'Security check failed.' ], 403 );
        }

        $slot_id = (int) ( $_POST['slot_id'] ?? 0 );
        $user = wp_get_current_user();
        if ( ! $user->ID || ! in_array( 'tc_speaker', (array) $user->roles, true ) ) {
            wp_send_json_error( [ 'message' => 'Please log in as an approved TechCon speaker before requesting a booking.', 'login' => home_url( '/speaker-login/' ) ], 403 );
        }
        if ( ! $slot_id ) wp_send_json_error( [ 'message' => 'Please select a valid slot.' ] );

        $slot = SlotsDAO::get_by_id( $slot_id );
        if ( ! $slot ) wp_send_json_error( [ 'message' => 'Slot not found.' ] );

        global $wpdb;
        $speaker_id = class_exists( '\TCA\Installer' ) ? (int) $wpdb->get_var( $wpdb->prepare( 'SELECT id FROM ' . \TCA\Installer::t_speakers() . ' WHERE wp_user_id=%d', $user->ID ) ) : 0;
        if ( ! $speaker_id ) wp_send_json_error( [ 'message' => 'Your login is not linked with an admin-created speaker profile.' ], 403 );

        $success = BookingsDAO::request_slot( $slot_id, $speaker_id );
        if ( ! $success ) {
            $reason = BookingsDAO::speaker_partner_day_lock_reason( (int) $slot['partner_id'], (string) $slot['slot_date'], $speaker_id );
            if ( empty( $reason['message'] ) ) {
                $reason = BookingsDAO::speaker_time_lock_reason(
                    (string) $slot['slot_date'],
                    (string) ( $slot['time_start'] ?? $slot['start_time'] ?? '' ),
                    (string) ( $slot['time_end'] ?? $slot['end_time'] ?? '' ),
                    $speaker_id,
                    $slot_id
                );
            }
            $message = ! empty( $reason['message'] ) ? $reason['message'] : 'Sorry, this slot was just taken or is no longer available. Please choose another.';
            wp_send_json_error( [ 'message' => $message ] );
        }

        EmailEngine::send_request_notification( $slot_id, $speaker_id );
        if ( get_option( 'tcb_booking_mode', 'simple' ) === 'simple' ) { EmailEngine::send_speaker_request_confirmation( $slot_id, $speaker_id ); }

        wp_send_json_success( [
            'message'    => get_option( 'tcb_booking_mode', 'simple' ) === 'simple' ? 'Your request has been sent to the media partner. If they accept it, the interview will be booked. If they reject it, only you will be blocked from requesting that exact slot again; the slot will remain available for other speakers.' : 'Your interview request has been submitted. Our team will review it and email you once it is confirmed. Your dashboard will also update automatically.',
            'booking_id' => $slot_id,
        ] );
    }
}
