<?php
/**
 * Email Notification Service
 *
 * Handles sending email notifications for bookings, confirmations, and admin alerts
 *
 * @since   PBOOKING_SINCE
 * @package Nemtly_Booking
 */

namespace NemtlyBooking\Services;

use NemtlyBooking\Models\Booking;
use NemtlyBooking\Models\Event;
use NemtlyBooking\Repositories\Event_Repository;
use NemtlyBooking\Core\Event_Manager;

/**
 * Email Notification Service class
 *
 * @since PBOOKING_SINCE
 */
class Email_Notification_Service {
    /**
     * Event repository
     *
     * @var Event_Repository
     */
    private $event_repository;
    /**
     * Event manager
     *
     * @var Event_Manager
     */
    private $event_manager;

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

    /**
     * Initialize hooks
     *
     * @since PBOOKING_SINCE
     */
    private function init_hooks() {
        // Listen for booking events
        add_action( 'nemtly_booking_event_booking_created', [ $this, 'send_booking_confirmation' ] );
        add_action( 'nemtly_booking_event_booking_confirmed', [ $this, 'send_booking_confirmed' ] );
        add_action( 'nemtly_booking_event_booking_cancelled', [ $this, 'send_booking_cancelled' ] );
        add_action( 'nemtly_booking_event_booking_waitlisted', [ $this, 'send_waitlist_confirmation' ] );
        // Admin notifications
        add_action( 'nemtly_booking_event_booking_created', [ $this, 'send_admin_notification' ] );
        // Cron for reminders
        add_action( 'nemtly_booking_hourly_reminder', [ $this, 'process_reminders' ] );
        // Magic Link
        add_action( 'nemtly_booking_send_magic_link', [ $this, 'send_magic_link_email' ], 10, 2 );
    }

    /**
     * Process email reminders
     *
     * @since PBOOKING_SINCE
     */
    public function process_reminders() {
        // Called by WP-Cron via the 'nemtly_booking_hourly_reminder' hook.
        // Runs once per hour to find upcoming confirmed bookings that haven't
        // received a reminder email yet and sends one within the configured
        // reminder window (e.g., 24 hours before the event).

        // Read reminder settings directly from options to avoid circular dependency.
        // Email_Notification_Service is constructed with Event_Repository + Event_Manager,
        // not Settings_Service — injecting it would create a dependency loop since
        // Settings_Service dispatches events that this service listens to.
        $options = get_option( 'nemtly_booking_settings', [] );
        $enabled = isset( $options['reminder_enabled'] ) ? (bool) $options['reminder_enabled'] : false;
        if ( ! $enabled ) {
            return;
        }
        $hours = isset( $options['reminder_hours'] ) ? intval( $options['reminder_hours'] ) : 24;

        // Booking_Repository is instantiated directly here because this service's
        // constructor only receives Event_Repository (for looking up event details).
        // TODO: Refactor constructor to accept Booking_Repository as a dependency.
        $booking_repo = new \NemtlyBooking\Repositories\Booking_Repository();
        $bookings     = $booking_repo->find_upcoming_confirmed_bookings( $hours );
        foreach ( $bookings as $booking ) {
            $meta = $booking->get_booking_meta();
            // Use booking_meta to track reminder state instead of a separate table.
            // This keeps the reminder logic self-contained within the booking record
            // and avoids needing a dedicated notifications-sent table.
            if ( ! empty( $meta['reminder_sent'] ) ) {
                continue;
            }
            $this->send_booking_reminder( $booking );
            // Mark reminder as sent so the next cron run skips this booking.
            $meta['reminder_sent']    = true;
            $meta['reminder_sent_at'] = gmdate( 'Y-m-d H:i:s' );
            $booking->set_booking_meta( $meta );
            $booking_repo->update( $booking );
        }
    }

    /**
     * Send booking reminder email to customer
     *
     * @since PBOOKING_SINCE
     *
     * @param Booking $booking Booking object.
     */
    public function send_booking_reminder( $booking ) {
        $event = $this->event_repository->find( $booking->get_event_id() );
        if ( ! $event ) {
            return;
        }
        $to      = $booking->get_attendee_email();
        $subject = $this->get_email_subject( 'reminder', $event, $booking );
        $message = $this->get_email_message( 'reminder', $event, $booking );
        $headers = $this->get_email_headers();
        $sent    = wp_mail( $to, $subject, $message, $headers );
        // Dispatch email sent event
        $this->event_manager->dispatch(
            'email.booking_reminder_sent',
            [
                'booking_id' => $booking->get_id(),
                'email'      => $to,
                'sent'       => $sent,
            ]
        );
    }

    /**
     * Send booking confirmation email to customer
     *
     * @since PBOOKING_SINCE
     *
     * @param array $booking_data Booking data from event.
     */
    public function send_booking_confirmation( $booking_data ) {
        $booking = new Booking( $booking_data );
        $event   = $this->event_repository->find( $booking->get_event_id() );
        if ( ! $event ) {
            return;
        }
        $to      = $booking->get_attendee_email();
        $subject = $this->get_email_subject( 'booking_confirmation', $event, $booking );
        $message = $this->get_email_message( 'booking_confirmation', $event, $booking );
        $headers = $this->get_email_headers();
        $sent    = wp_mail( $to, $subject, $message, $headers );
        // Dispatch email sent event
        $this->event_manager->dispatch(
            'email.booking_confirmation_sent',
            [
                'booking_id' => $booking->get_id(),
                'email'      => $to,
                'sent'       => $sent,
            ]
        );
    }

    /**
     * Send booking confirmed email to customer
     *
     * @since PBOOKING_SINCE
     *
     * @param array $booking_data Booking data from event.
     */
    public function send_booking_confirmed( $booking_data ) {
        $booking = new Booking( $booking_data );
        $event   = $this->event_repository->find( $booking->get_event_id() );
        if ( ! $event ) {
            return;
        }
        $to      = $booking->get_attendee_email();
        $subject = $this->get_email_subject( 'booking_confirmed', $event, $booking );
        $message = $this->get_email_message( 'booking_confirmed', $event, $booking );
        $headers = $this->get_email_headers();
        $sent    = wp_mail( $to, $subject, $message, $headers );
        // Dispatch email sent event
        $this->event_manager->dispatch(
            'email.booking_confirmed_sent',
            [
                'booking_id' => $booking->get_id(),
                'email'      => $to,
                'sent'       => $sent,
            ]
        );
    }

    /**
     * Send booking cancelled email to customer
     *
     * @since PBOOKING_SINCE
     *
     * @param array $booking_data Booking data from event.
     */
    public function send_booking_cancelled( $booking_data ) {
        $booking = new Booking( $booking_data );
        $event   = $this->event_repository->find( $booking->get_event_id() );
        if ( ! $event ) {
            return;
        }
        $to      = $booking->get_attendee_email();
        $subject = $this->get_email_subject( 'booking_cancelled', $event, $booking );
        $message = $this->get_email_message( 'booking_cancelled', $event, $booking );
        $headers = $this->get_email_headers();
        $sent    = wp_mail( $to, $subject, $message, $headers );
        // Dispatch email sent event
        $this->event_manager->dispatch(
            'email.booking_cancelled_sent',
            [
                'booking_id' => $booking->get_id(),
                'email'      => $to,
                'sent'       => $sent,
            ]
        );
    }

    /**
     * Send waitlist confirmation email to the customer.
     *
     * Confirms the visitor has been added to the waitlist and that they will
     * be contacted if a spot becomes available.
     *
     * @since PBOOKING_SINCE
     *
     * @param array $booking_data Booking data from the waitlist event.
     */
    public function send_waitlist_confirmation( $booking_data ) {
        $booking = new Booking( $booking_data );
        $event   = $this->event_repository->find( $booking->get_event_id() );
        if ( ! $event ) {
            return;
        }

        $to      = $booking->get_attendee_email();
        $subject = sprintf(
            /* translators: %s: event title. */
            __( 'You are on the waitlist for %s', 'nemtly-booking' ),
            $event->get_title()
        );

        $message = sprintf(
            "%s\n\n%s\n\n%s\n",
            sprintf(
                /* translators: 1: attendee name, 2: event title. */
                __( 'Hi %1$s, you have been added to the waitlist for "%2$s".', 'nemtly-booking' ),
                $booking->get_attendee_name(),
                $event->get_title()
            ),
            __( 'If a spot becomes available we will contact you at this email address with instructions to confirm your booking.', 'nemtly-booking' ),
            get_bloginfo( 'name' )
        );

        $headers = $this->get_email_headers();
        $sent    = wp_mail( $to, $subject, $message, $headers );

        $this->event_manager->dispatch(
            'email.waitlist_confirmation_sent',
            [
                'booking_id' => $booking->get_id(),
                'email'      => $to,
                'sent'       => $sent,
            ]
        );
    }

    /**
     * Send magic link email
     *
     * @since PBOOKING_SINCE
     *
     * @param string $email User email.
     * @param string $token Magic link token.
     */
    public function send_magic_link_email( $email, $token ) {
        // We need an instance of Token_Service to generate the URL.
        // For now, we'll instantiate it or assume we know how to build it.
        // Ideally, inject Token_Service, but Email_Notification_Service is already heavy.
        // Let's use a helper or instantiate.
        $token_service = new Token_Service();
        // Find the page with the dashboard shortcode?
        // For MVP, assume it's the current referrer or Home.
        // Actually best practice: Settings option for "Dashboard Page".
        // Fallback: Home URL or the Referer if it contains the shortcode (hard to know).
        // Let's use site url for now or try to find page with shortcode.
        // Simple approach: Use home_url() and trust the shortcode handles it if placed there,
        // OR rely on a setting.
        // Let's get the page ID from settings if available, else maybe the user is already on the page?
        // The Token_Service::get_magic_link_url needs a base URL.
        // Let's use the REFERER stored in the request handler? No.
        // Better: Query for the page containing the shortcode.
        // This is expensive.
        // Let's just use home_url() for now, and user can navigate?
        // No, that breaks the "Magic" part.
        // Let's assume there is a specific page for dashboard.
        // I will assume the user has set up a page.
        // Let's create a helper to find the page or default to home.
        $dashboard_url = $this->get_dashboard_url();
        $magic_link    = $token_service->get_magic_link_url( $token, $dashboard_url );
        $to            = $email;
        $subject       = sprintf(
            // Translators: %s is the site name
            __( '[%s] Your Magic Link', 'nemtly-booking' ),
            get_bloginfo( 'name' )
        );
        $message = sprintf(
            __( "Hello,\n\nClick the link below to access your booking dashboard:\n\n%1\$s\n\nThis link will expire in 15 minutes.\n\nBest regards,\n%2\$s", 'nemtly-booking' ),
            $magic_link,
            get_bloginfo( 'name' )
        );
        $headers = $this->get_email_headers();
        wp_mail( $to, $subject, $message, $headers );
    }

    /**
     * Get dashboard URL.
     *
     * Prefers the explicitly configured page (Settings → Pages → Customer
     * dashboard page). Falls back to a one-time SQL search for any published
     * page containing the [nemtly_booking_dashboard] shortcode, and finally
     * to a /dashboard/ guess.
     *
     * @since PBOOKING_SINCE
     *
     * @return string Dashboard URL.
     */
    private function get_dashboard_url() {
        $settings_service  = new \NemtlyBooking\Services\Settings_Service();
        $dashboard_page_id = (int) $settings_service->get_setting( 'dashboard_page_id', 0 );

        if ( $dashboard_page_id > 0 ) {
            $permalink = get_permalink( $dashboard_page_id );
            if ( $permalink ) {
                return $permalink;
            }
        }

        // Fallback: scan posts for the shortcode (covers installs that skipped
        // the setup wizard or never picked a page in Settings).
        global $wpdb;
        $page_id = $wpdb->get_var( 'SELECT ID FROM ' . $wpdb->posts . ' WHERE post_content LIKE "%[nemtly_booking_dashboard]%" AND post_status = "publish" LIMIT 1' );
        if ( $page_id ) {
            return get_permalink( $page_id );
        }

        return home_url( '/dashboard/' );
    }

    /**
     * Send admin notification for new bookings
     *
     * @since PBOOKING_SINCE
     *
     * @param array $booking_data Booking data from event.
     */
    public function send_admin_notification( $booking_data ) {
        $booking     = new Booking( $booking_data );
        $event       = $this->event_repository->find( $booking->get_event_id() );
        $admin_email = get_option( 'admin_email' );
        if ( ! $event || ! $admin_email ) {
            return;
        }
        $to      = $admin_email;
        $subject = $this->get_email_subject( 'admin_notification', $event, $booking );
        $message = $this->get_email_message( 'admin_notification', $event, $booking );
        $headers = $this->get_email_headers();
        $sent    = wp_mail( $to, $subject, $message, $headers );
        // Dispatch email sent event
        $this->event_manager->dispatch(
            'email.admin_notification_sent',
            [
                'booking_id' => $booking->get_id(),
                'email'      => $to,
                'sent'       => $sent,
            ]
        );
    }

    /**
     * Get email subject based on type
     *
     * @since PBOOKING_SINCE
     *
     * @param string  $type    Email type.
     * @param Event   $event   Event object.
     * @param Booking $booking Booking object.
     *
     * @return string Email subject.
     */
    private function get_email_subject( $type, Event $event, Booking $booking ) {
        $site_name = get_bloginfo( 'name' );
        switch ( $type ) {
            case 'booking_confirmation':
                $subject = sprintf(
                    /* translators: %1$s: Site name, %2$s: Event title */
                    __( '[%1$s] Booking Confirmation - %2$s', 'nemtly-booking' ),
                    $site_name,
                    $event->get_title()
                );
                break;
            case 'booking_confirmed':
                $subject = sprintf(
                    /* translators: %1$s: Site name, %2$s: Event title */
                    __( '[%1$s] Booking Confirmed - %2$s', 'nemtly-booking' ),
                    $site_name,
                    $event->get_title()
                );
                break;
            case 'booking_cancelled':
                $subject = sprintf(
                    /* translators: %1$s: Site name, %2$s: Event title */
                    __( '[%1$s] Booking Cancelled - %2$s', 'nemtly-booking' ),
                    $site_name,
                    $event->get_title()
                );
                break;
            case 'admin_notification':
                $subject = sprintf(
                    /* translators: %1$s: Site name, %2$s: Event title */
                    __( '[%1$s] New Booking - %2$s', 'nemtly-booking' ),
                    $site_name,
                    $event->get_title()
                );
                break;
            case 'reminder':
                $settings_service = new \NemtlyBooking\Services\Settings_Service();
                $email_settings   = $settings_service->get_email_settings();
                $subject          = $email_settings['reminder_subject'];
                // Replace placeholders in subject if any
                $placeholders = $this->get_email_placeholders( $event, $booking );
                foreach ( $placeholders as $placeholder => $value ) {
                    $subject = str_replace( "{{$placeholder}}", $value, $subject );
                }
                break;
            default:
                $subject = sprintf(
                    /* translators: %1$s: Site name */
                    __( '[%1$s] Booking Update', 'nemtly-booking' ),
                    $site_name
                );
                break;
        }
        return apply_filters( "nemtly_booking_email_subject_{$type}", $subject, $event, $booking );
    }

    /**
     * Get email message based on type
     *
     * @since PBOOKING_SINCE
     *
     * @param string  $type    Email type.
     * @param Event   $event   Event object.
     * @param Booking $booking Booking object.
     *
     * @return string Email message.
     */
    private function get_email_message( $type, Event $event, Booking $booking ) {
        $message = $this->get_email_template( $type );
        // Replace placeholders
        $placeholders = $this->get_email_placeholders( $event, $booking );
        foreach ( $placeholders as $placeholder => $value ) {
            $message = str_replace( "{{$placeholder}}", $value, $message );
        }
        return apply_filters( "nemtly_booking_email_message_{$type}", $message, $event, $booking, $placeholders );
    }

    /**
     * Get email template based on type
     *
     * @since PBOOKING_SINCE
     *
     * @param string $type Email type.
     *
     * @return string Email template.
     */
    private function get_email_template( $type ) {
        switch ( $type ) {
            case 'booking_confirmation':
                return $this->get_booking_confirmation_template();
            case 'booking_confirmed':
                return $this->get_booking_confirmed_template();
            case 'booking_cancelled':
                return $this->get_booking_cancelled_template();
            case 'admin_notification':
                return $this->get_admin_notification_template();
            case 'reminder':
                $settings_service = new \NemtlyBooking\Services\Settings_Service();
                $email_settings   = $settings_service->get_email_settings();
                return $email_settings['reminder_message'];
            default:
                return __( 'Your booking has been updated.', 'nemtly-booking' );
        }
    }

    /**
     * Get booking confirmation email template
     *
     * @since PBOOKING_SINCE
     *
     * @return string Email template.
     */
    private function get_booking_confirmation_template() {
        return __(
            'Hello {attendee_name},

Thank you for your booking! This email confirms your reservation for:

Event: {event_title}
Date: {event_date}
Time: {event_time}
Location: {event_location}

Booking Details:
- Booking Reference: {booking_uuid}
- Status: {booking_status}

Your booking is currently pending confirmation. We will send you another email once it has been confirmed.

If you need to cancel or modify your booking, please contact us.

Best regards,
{site_name}',
            'nemtly-booking'
        );
    }

    /**
     * Get booking confirmed email template
     *
     * @since PBOOKING_SINCE
     *
     * @return string Email template.
     */
    private function get_booking_confirmed_template() {
        return __(
            'Hello {attendee_name},

Great news! Your booking has been confirmed:

Event: {event_title}
Date: {event_date}
Time: {event_time}
Location: {event_location}

Booking Details:
- Booking Reference: {booking_uuid}
- Status: Confirmed

We look forward to seeing you at the event!

Best regards,
{site_name}',
            'nemtly-booking'
        );
    }

    /**
     * Get booking cancelled email template
     *
     * @since PBOOKING_SINCE
     *
     * @return string Email template.
     */
    private function get_booking_cancelled_template() {
        return __(
            'Hello {attendee_name},

This email confirms that your booking has been cancelled:

Event: {event_title}
Date: {event_date}
Time: {event_time}

Booking Details:
- Booking Reference: {booking_uuid}
- Status: Cancelled

If you have any questions or would like to make a new booking, please feel free to contact us.

Best regards,
{site_name}',
            'nemtly-booking'
        );
    }

    /**
     * Get admin notification email template
     *
     * @since PBOOKING_SINCE
     *
     * @return string Email template.
     */
    private function get_admin_notification_template() {
        return __(
            'New Booking Received

A new booking has been made for:

Event: {event_title}
Date: {event_date}
Time: {event_time}

Attendee Details:
- Name: {attendee_name}
- Email: {attendee_email}
- Phone: {attendee_phone}

Booking Details:
- Booking Reference: {booking_uuid}
- Status: {booking_status}
- Booking Date: {booking_date}

Please log in to your admin dashboard to review and confirm this booking.

{site_name}',
            'nemtly-booking'
        );
    }

    /**
     * Get email placeholders
     *
     * @since PBOOKING_SINCE
     *
     * @param Event   $event   Event object.
     * @param Booking $booking Booking object.
     *
     * @return array Placeholders array.
     */
    private function get_email_placeholders( Event $event, Booking $booking ) {
        $event_location = get_post_meta( $event->get_id(), 'nemtly_location', true );
        $attendee_phone = $booking->get_attendee_phone();

        // Use timezone-aware formatting for date/time display in emails
        $formatted_date = $event->get_formatted_date();
        $formatted_time = $event->get_formatted_time();

        return [
            'site_name'       => get_bloginfo( 'name' ),
            'site_url'        => get_bloginfo( 'url' ),
            'attendee_name'   => $booking->get_attendee_name(),
            'attendee_email'  => $booking->get_attendee_email(),
            'attendee_phone'  => ! empty( $attendee_phone ) ? $attendee_phone : __( 'Not provided', 'nemtly-booking' ),
            'event_title'     => $event->get_title(),
            'event_date'      => $formatted_date,
            'event_time'      => $formatted_time,
            'event_location'  => ! empty( $event_location ) ? $event_location : __( 'Not specified', 'nemtly-booking' ),
            'booking_uuid'    => $booking->get_booking_uuid(),
            'booking_status'  => ucfirst( $booking->get_booking_status() ),
            'booking_date'    => $booking->get_booking_date(),
            'booking_created' => $booking->get_created_at(),
        ];
    }

    /**
     * Get email headers
     *
     * @since PBOOKING_SINCE
     *
     * @return array Email headers.
     */
    private function get_email_headers() {
        $admin_email = get_option( 'admin_email' );
        $site_name   = get_bloginfo( 'name' );
        $headers     = [
            'Content-Type: text/plain; charset=UTF-8',
            'From: ' . $site_name . ' <' . $admin_email . '>',
        ];
        return apply_filters( 'nemtly_booking_email_headers', $headers );
    }
}
