<?php
/**
 * Yatoon Booking System - PWA Support
 *
 * Handles:
 *  - web app manifest (customer booking side)
 *  - web app manifest (merchant admin side)
 *  - Service Worker registration for both sides
 *  - <meta> tags for iOS/Android installability
 *  - SW proxy endpoint (serves sw-admin.js from /wp-admin/ scope)
 *  - Push notification subscription endpoint (future use)
 */
if ( ! defined( 'ABSPATH' ) ) exit;

class YATOON_PWA {

    public function __construct() {
        // -- Customer booking side --------------------------------------------
        add_action( 'wp_head',                array( $this, 'booking_pwa_meta' ), 1 );
        add_action( 'wp_enqueue_scripts',     array( $this, 'booking_register_sw' ) );

        // -- Merchant admin side ----------------------------------------------
        add_action( 'admin_head',             array( $this, 'admin_pwa_meta' ) );
        add_action( 'admin_enqueue_scripts',  array( $this, 'admin_register_sw' ) );

        // -- REST endpoints ---------------------------------------------------
        add_action( 'rest_api_init',          array( $this, 'register_rest_routes' ) );

        // -- Rewrite rule: serve manifest + SW files via WP -------------------
        add_action( 'init',                   array( $this, 'add_rewrite_rules' ) );
        add_filter( 'query_vars',             array( $this, 'add_query_vars' ) );
        add_action( 'template_redirect',      array( $this, 'serve_pwa_files' ) );

        // -- Admin: SW proxy served from /wp-admin/ path -----------------------
        add_action( 'admin_init',             array( $this, 'serve_admin_sw_proxy' ) );
    }
    // CUSTOMER BOOKING SIDE

    /**
     * Inject PWA <meta> + <link> tags into booking pages only.
     */
    public function booking_pwa_meta() {
        if ( ! $this->is_booking_page() ) return;

        $name        = esc_attr( get_bloginfo( 'name' ) . ' - Book Now' );
        $color       = esc_attr( get_option( 'yatoon_primary_color', '#ec4899' ) );
        $manifest    = esc_url( home_url( '/yatoon-booking-manifest.json' ) );
        $icon_192    = esc_url( YATOON_PLUGIN_URL . 'assets/images/pwa-icon-192.png' );
        ?>
        <!-- Yatoon PWA: Booking Side -->
        <link rel="manifest" href="<?php echo esc_url( $manifest ); ?>">
        <meta name="theme-color" content="<?php echo esc_attr( $color ); ?>">
        <meta name="mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-status-bar-style" content="default">
        <meta name="apple-mobile-web-app-title" content="<?php echo esc_attr( $name ); ?>">
        <link rel="apple-touch-icon" href="<?php echo esc_url( $icon_192 ); ?>">
        <?php
    }

    /**
     * Register and boot the customer-side Service Worker.
     */
    public function booking_register_sw() {
        if ( ! $this->is_booking_page() ) return;

        $sw_url = esc_url( home_url( '/yatoon-booking-sw.js' ) );
        $scope  = esc_url( $this->get_booking_scope() );
        wp_add_inline_script(
            'sbs-frontend-js',
            "if ('serviceWorker' in navigator) {
                navigator.serviceWorker.getRegistrations().then(function(registrations) {
                    registrations.forEach(function(registration) {
                        var worker = registration.active || registration.waiting || registration.installing;
                        if (registration.scope === window.location.origin + '/' && worker && worker.scriptURL.indexOf('yatoon-') !== -1) {
                            registration.unregister();
                        }
                    });
                });
                navigator.serviceWorker.register('" . $sw_url . "', { scope: '" . $scope . "' })
                    .then(function() {})
                    .catch(function() {});
            }"
        );
    }
    // MERCHANT ADMIN SIDE

    /**
     * Inject PWA <meta> + <link> tags into Yatoon admin pages only.
     */
    public function admin_pwa_meta() {
        if ( ! $this->is_yatoon_admin_page() ) return;

        $name     = esc_attr( get_bloginfo( 'name' ) . ' - Yatoon Admin' );
        $color    = esc_attr( get_option( 'yatoon_primary_color', '#ec4899' ) );
        $manifest = esc_url( home_url( '/yatoon-admin-manifest.json' ) );
        $icon_192 = esc_url( YATOON_PLUGIN_URL . 'assets/images/pwa-icon-192.png' );
        ?>
        <!-- Yatoon PWA: Admin Side -->
        <link rel="manifest" href="<?php echo esc_url( $manifest ); ?>">
        <meta name="theme-color" content="<?php echo esc_attr( $color ); ?>">
        <meta name="mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-status-bar-style" content="default">
        <meta name="apple-mobile-web-app-title" content="<?php echo esc_attr( $name ); ?>">
        <link rel="apple-touch-icon" href="<?php echo esc_url( $icon_192 ); ?>">
        <?php
    }

    /**
     * Register and boot the admin-side Service Worker.
     * The SW file must be served from /wp-admin/ to get that scope.
     */
    public function admin_register_sw() {
        if ( ! $this->is_yatoon_admin_page() ) return;

        // SW is proxied at /wp-admin/?yatoon_admin_sw=1
        $sw_url    = esc_url( admin_url( '?yatoon_admin_sw=1' ) );
        $admin_url = esc_url( admin_url( 'admin.php?page=yatoon-booking-system' ) );

        wp_add_inline_script(
            'sbs-admin-js',
            "if ('serviceWorker' in navigator) {
                navigator.serviceWorker.register('" . $sw_url . "', { scope: '/wp-admin/' })
                    .then(function(reg) {
                        yatoonRequestPushPermission(reg);
                    })
                    .catch(function() {});
            }

            function yatoonRequestPushPermission(reg) {
                if (!('PushManager' in window) || !('Notification' in window)) return;
                if (Notification.permission === 'granted') return;
                if (Notification.permission === 'denied') return;
                // Only ask after user interaction - attach to first click in admin
                document.addEventListener('click', function askOnce() {
                    Notification.requestPermission().then(function(perm) {
                    });
                    document.removeEventListener('click', askOnce);
                }, { once: true });
            }"
        );
    }

    /**
     * Serve the admin SW JS file from /wp-admin/ scope via query param proxy.
     */
    public function serve_admin_sw_proxy() {
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        if ( ! isset( $_GET['yatoon_admin_sw'] ) ) return;
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( 'Forbidden', 403 );
        }

        $sw_file = YATOON_PLUGIN_DIR . 'assets/js/sw-admin.js';
        if ( ! file_exists( $sw_file ) ) {
            status_header( 404 );
            exit;
        }

        header( 'Content-Type: application/javascript; charset=utf-8' );
        header( 'Cache-Control: no-cache, no-store, must-revalidate' );
        header( 'Service-Worker-Allowed: /wp-admin/' );
        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents,WordPress.Security.EscapeOutput.OutputNotEscaped
        echo file_get_contents( $sw_file ); // nosemgrep: file-inclusion
        exit;
    }
    // MANIFEST AND SERVICE WORKER FILE SERVING

    public function add_rewrite_rules() {
        add_rewrite_rule( '^yatoon-booking-manifest\.json$', 'index.php?yatoon_pwa_file=booking-manifest', 'top' );
        add_rewrite_rule( '^yatoon-admin-manifest\.json$',   'index.php?yatoon_pwa_file=admin-manifest',   'top' );
        add_rewrite_rule( '^yatoon-booking-sw\.js$',          'index.php?yatoon_pwa_file=booking-sw',       'top' );
    }

    public function add_query_vars( $vars ) {
        $vars[] = 'yatoon_pwa_file';
        return $vars;
    }

    public function serve_pwa_files() {
        $file = get_query_var( 'yatoon_pwa_file' );
        if ( ! $file ) return;

        $site_name    = html_entity_decode( get_bloginfo( 'name' ), ENT_QUOTES, 'UTF-8' );
        $primary      = get_option( 'yatoon_primary_color', '#ec4899' );
        $icon_192     = YATOON_PLUGIN_URL . 'assets/images/pwa-icon-192.png';
        $icon_512     = YATOON_PLUGIN_URL . 'assets/images/pwa-icon-512.png';
        $booking_url  = $this->get_booking_page_url();

        switch ( $file ) {

            case 'booking-manifest':
                header( 'Content-Type: application/manifest+json; charset=utf-8' );
                header( 'Cache-Control: no-cache' );
                $manifest = array(
                    'name'             => $site_name . ' - Book Now',
                    'short_name'       => 'Book Now',
                    'description'      => 'Book your appointment at ' . $site_name,
                    'start_url'        => $booking_url,
                    'scope'            => $this->get_booking_scope(),
                    'display'          => 'standalone',
                    'background_color' => '#ffffff',
                    'theme_color'      => $primary,
                    'orientation'      => 'portrait-primary',
                    'lang'             => get_bloginfo( 'language' ),
                    'icons'            => array(
                        array( 'src' => $icon_192, 'sizes' => '192x192', 'type' => 'image/png', 'purpose' => 'any maskable' ),
                        array( 'src' => $icon_512, 'sizes' => '512x512', 'type' => 'image/png', 'purpose' => 'any maskable' ),
                    ),
                    'screenshots'      => array(),
                    'categories'       => array( 'beauty', 'lifestyle' ),
                );
                // phpcs:ignore WordPress.WP.AlternativeFunctions.json_encode_json_encode,WordPress.Security.EscapeOutput.OutputNotEscaped
                echo wp_json_encode( $manifest, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
                exit;

            case 'admin-manifest':
                header( 'Content-Type: application/manifest+json; charset=utf-8' );
                header( 'Cache-Control: no-cache' );
                $admin_start = admin_url( 'admin.php?page=yatoon-booking-system' );
                $manifest = array(
                    'name'             => $site_name . ' - Yatoon Admin',
                    'short_name'       => 'Yatoon',
                    'description'      => 'Manage bookings for ' . $site_name,
                    'start_url'        => $admin_start,
                    'scope'            => '/wp-admin/',
                    'display'          => 'standalone',
                    'background_color' => '#1e1e2e',
                    'theme_color'      => $primary,
                    'orientation'      => 'portrait-primary',
                    'lang'             => get_bloginfo( 'language' ),
                    'icons'            => array(
                        array( 'src' => $icon_192, 'sizes' => '192x192', 'type' => 'image/png', 'purpose' => 'any maskable' ),
                        array( 'src' => $icon_512, 'sizes' => '512x512', 'type' => 'image/png', 'purpose' => 'any maskable' ),
                    ),
                    'categories'       => array( 'business', 'productivity' ),
                );
                // phpcs:ignore WordPress.WP.AlternativeFunctions.json_encode_json_encode,WordPress.Security.EscapeOutput.OutputNotEscaped
                echo wp_json_encode( $manifest, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
                exit;

            case 'booking-sw':
                $sw_file = YATOON_PLUGIN_DIR . 'assets/js/sw-booking.js';
                if ( ! file_exists( $sw_file ) ) {
                    status_header( 404 );
                    exit;
                }
                header( 'Content-Type: application/javascript; charset=utf-8' );
                header( 'Cache-Control: no-cache, no-store, must-revalidate' );
                header( 'Service-Worker-Allowed: ' . $this->get_booking_scope() );
                // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents,WordPress.Security.EscapeOutput.OutputNotEscaped
                echo file_get_contents( $sw_file );
                exit;
        }
    }

    // ════════════════════════════════════════════════════════════════════════
    // REST API (Push subscription - future use)
    // ════════════════════════════════════════════════════════════════════════

    public function register_rest_routes() {
        register_rest_route( 'yatoon/v1', '/pwa/subscribe', array(
            'methods'             => 'POST',
            'callback'            => array( $this, 'rest_save_push_subscription' ),
            'permission_callback' => function() { return current_user_can( 'manage_options' ); },
            'args'                => array(
                'endpoint' => array( 'required' => true, 'sanitize_callback' => 'esc_url_raw' ),
                'keys'     => array( 'required' => true ),
            ),
        ) );

        register_rest_route( 'yatoon/v1', '/pwa/unsubscribe', array(
            'methods'             => 'POST',
            'callback'            => array( $this, 'rest_remove_push_subscription' ),
            'permission_callback' => function() { return current_user_can( 'manage_options' ); },
        ) );
    }

    public function rest_save_push_subscription( WP_REST_Request $request ) {
        $user_id  = get_current_user_id();
        $endpoint = $request->get_param( 'endpoint' );
        $keys     = $request->get_param( 'keys' );

        $subscriptions   = get_user_meta( $user_id, 'yatoon_push_subscriptions', true );
        $subscriptions   = is_array( $subscriptions ) ? $subscriptions : array();
        $subscriptions[] = array(
            'endpoint' => $endpoint,
            'keys'     => $keys,
            'created'  => current_time( 'mysql' ),
        );

        update_user_meta( $user_id, 'yatoon_push_subscriptions', $subscriptions );
        return rest_ensure_response( array( 'success' => true ) );
    }

    public function rest_remove_push_subscription( WP_REST_Request $request ) {
        $user_id  = get_current_user_id();
        $endpoint = $request->get_param( 'endpoint' );

        $subscriptions = get_user_meta( $user_id, 'yatoon_push_subscriptions', true );
        if ( is_array( $subscriptions ) ) {
            $subscriptions = array_filter( $subscriptions, function( $s ) use ( $endpoint ) {
                return $s['endpoint'] !== $endpoint;
            } );
            update_user_meta( $user_id, 'yatoon_push_subscriptions', array_values( $subscriptions ) );
        }

        return rest_ensure_response( array( 'success' => true ) );
    }

    // ════════════════════════════════════════════════════════════════════════
    // HELPERS
    // ════════════════════════════════════════════════════════════════════════

    private function is_booking_page() {
        if ( ! is_singular() ) return false;

        $queried_id = get_queried_object_id();
        if ( ! $queried_id ) return false;

        // Preferred: match against the page ID saved in settings. This works
        // regardless of how the page was built (Brizy, Elementor, classic
        // editor, etc.) since page builders often don't keep the literal
        // shortcode text in post_content, which breaks has_shortcode() checks.
        $booking_page_id = absint( get_option( 'yatoon_booking_page_id', 0 ) );
        $manage_page_id  = absint( get_option( 'yatoon_manage_booking_page_id', 0 ) );
        if ( ( $booking_page_id && $queried_id === $booking_page_id )
            || ( $manage_page_id && $queried_id === $manage_page_id ) ) {
            return true;
        }

        // Fallback: literal shortcode text in post_content (classic editor pages).
        global $post;
        if ( ! $post ) return false;
        return has_shortcode( $post->post_content, 'yatoon_booking' )
            || has_shortcode( $post->post_content, 'yatoon_customer_portal' )
            || has_shortcode( $post->post_content, 'yatoon_manage_booking' );
    }

    /**
     * Resolve the real URL of the page where [yatoon_booking] lives, instead of
     * always falling back to the homepage. Prefers the page ID saved during
     * onboarding (yatoon_booking_page_id); falls back to a shortcode search;
     * only uses home_url('/') if nothing else can be found.
     */
    private function get_booking_page_url() {
        $page_id = absint( get_option( 'yatoon_booking_page_id', 0 ) );
        if ( $page_id && get_post_status( $page_id ) === 'publish' ) {
            return get_permalink( $page_id );
        }

        global $wpdb;
        $found_id = yatoon_db_get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
            "SELECT ID FROM {$wpdb->posts} WHERE post_status='publish' AND post_content LIKE '%[yatoon_booking]%' LIMIT 1"
        );
        if ( $found_id ) {
            return get_permalink( (int) $found_id );
        }

        return home_url( '/' );
    }

    private function get_booking_scope() {
        $path = wp_parse_url( $this->get_booking_page_url(), PHP_URL_PATH );
        return trailingslashit( $path ? $path : '/' );
    }

    private function is_yatoon_admin_page() {
        if ( ! is_admin() ) return false;
        return class_exists( 'YATOON_App_Shell' ) && YATOON_App_Shell::is_booking_admin_page();
    }

    /**
     * Helper: send a push notification to all subscribed admin users.
     * Called from YATOON_Ajax when a new booking is created.
     *
     * @param array $payload  Keys: title, body, url, booking_id
     */
    public static function send_push_to_admins( array $payload ) {
        // Get all users with manage_options
        $admins = get_users( array( 'role__in' => array( 'administrator' ) ) );
        foreach ( $admins as $admin ) {
            $subscriptions = get_user_meta( $admin->ID, 'yatoon_push_subscriptions', true );
            if ( ! is_array( $subscriptions ) || empty( $subscriptions ) ) continue;
            foreach ( $subscriptions as $sub ) {
                // Push sending requires a server-side VAPID library (web-push-php).
                // Subscriptions are stored here; actual sending is done via the
                // Cloudflare Worker (yatoon-api.yatoon.workers.dev/push) to keep
                // the plugin dependency-free. Worker forwards the payload using
                // the VAPID keys configured in the Worker environment.
                wp_remote_post( YATOON_WORKER_URL . '/push', array(
                    'body'    => wp_json_encode( array(
                        'subscription' => $sub,
                        'payload'      => $payload,
                    ) ),
                    'headers' => array(
                        'Content-Type'  => 'application/json',
                        'Authorization' => 'Bearer ' . YATOON_WORKER_SECRET,
                    ),
                    'blocking' => false, // fire and forget
                ) );
            }
        }
    }
}
