<?php

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

class AIEO_Push_REST {

    const NAMESPACE_V1 = 'aieo/v1';

    
    private static $instance = null;

    public static function instance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    private function __construct() {
        add_action('rest_api_init', array($this, 'register_routes'));
    }

    public function register_routes() {
        register_rest_route(self::NAMESPACE_V1, '/push/subscribe', array(
            'methods'             => \WP_REST_Server::CREATABLE,
            'callback'            => array($this, 'handle_subscribe'),
            'permission_callback' => '__return_true',
            'args'                => array(
                'endpoint' => array(
                    'required'          => true,
                    'sanitize_callback' => 'esc_url_raw',
                ),
                'p256dh' => array(
                    'required'          => true,
                    'sanitize_callback' => array($this, 'sanitize_b64url'),
                ),
                'auth' => array(
                    'required'          => true,
                    'sanitize_callback' => array($this, 'sanitize_b64url'),
                ),
                'previous_endpoint' => array(
                    'required'          => false,
                    'sanitize_callback' => 'esc_url_raw',
                ),
            ),
        ));

        register_rest_route(self::NAMESPACE_V1, '/push/unsubscribe', array(
            'methods'             => \WP_REST_Server::CREATABLE,
            'callback'            => array($this, 'handle_unsubscribe'),
            'permission_callback' => '__return_true',
            'args'                => array(
                'endpoint' => array(
                    'required'          => true,
                    'sanitize_callback' => 'esc_url_raw',
                ),
            ),
        ));

        register_rest_route(self::NAMESPACE_V1, '/push/event', array(
            'methods'             => \WP_REST_Server::CREATABLE,
            'callback'            => array($this, 'handle_event'),
            'permission_callback' => '__return_true',
            'args'                => array(
                'dispatch_id' => array(
                    'required'          => true,
                    'sanitize_callback' => 'absint',
                ),
                'kind' => array(
                    'required'          => true,
                    'sanitize_callback' => 'sanitize_key',
                ),
            ),
        ));
    }

    
    public function sanitize_b64url($value) {
        return preg_replace('/[^A-Za-z0-9_\-=]/', '', (string) $value);
    }

    
    
    

    public function handle_subscribe($req) {
        global $wpdb;

        $endpoint = (string) $req->get_param('endpoint');
        $p256dh   = (string) $req->get_param('p256dh');
        $auth     = (string) $req->get_param('auth');
        $prev     = (string) $req->get_param('previous_endpoint');

        if ($endpoint === '' || $p256dh === '' || $auth === '') {
            return new \WP_REST_Response(array('ok' => false, 'error' => 'missing-fields'), 400);
        }

        $endpoint_hash = hash('sha256', $endpoint);
        $user_id       = (int) get_current_user_id();
        $session_token = $this->get_or_create_session_token();
        $user_agent    = isset($_SERVER['HTTP_USER_AGENT']) ? substr((string) wp_unslash($_SERVER['HTTP_USER_AGENT']), 0, 255) : '';
        $device_kind   = $this->guess_device_kind($user_agent);
        $locale        = $this->detect_locale();
        $tz_offset     = isset($_SERVER['HTTP_X_AIEO_TZ_OFFSET']) ? (int) $_SERVER['HTTP_X_AIEO_TZ_OFFSET'] : 0;

        $table = $wpdb->prefix . 'aieo_push_subscriptions';

        
        
        
        if ($prev !== '' && $prev !== $endpoint) {
            $wpdb->update(
                $table,
                array('status' => 'revoked', 'bounced_at' => current_time('mysql', true)),
                array('endpoint_hash' => hash('sha256', $prev)),
                array('%s', '%s'),
                array('%s')
            );
        }

        $now = current_time('mysql', true);

        
        
        
        $wpdb->query($wpdb->prepare(
            "INSERT INTO {$table}
                (user_id, session_token, endpoint_hash, endpoint, p256dh, auth_secret,
                 user_agent, device_kind, locale, tz_offset_minutes, status, subscribed_at)
             VALUES (%d, %s, %s, %s, %s, %s, %s, %s, %s, %d, 'active', %s)
             ON DUPLICATE KEY UPDATE
                user_id           = VALUES(user_id),
                session_token     = VALUES(session_token),
                endpoint          = VALUES(endpoint),
                p256dh            = VALUES(p256dh),
                auth_secret       = VALUES(auth_secret),
                user_agent        = VALUES(user_agent),
                device_kind       = VALUES(device_kind),
                locale            = VALUES(locale),
                tz_offset_minutes = VALUES(tz_offset_minutes),
                status            = 'active',
                bounced_at        = NULL",
            $user_id,
            $session_token,
            $endpoint_hash,
            $endpoint,
            $p256dh,
            $auth,
            $user_agent,
            $device_kind,
            $locale,
            $tz_offset,
            $now
        ));

        $sub_id = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT subscription_id FROM {$table} WHERE endpoint_hash = %s LIMIT 1",
            $endpoint_hash
        ));

        return new \WP_REST_Response(array('ok' => true, 'subscription_id' => $sub_id), 200);
    }

    
    
    

    public function handle_unsubscribe($req) {
        global $wpdb;
        $endpoint = (string) $req->get_param('endpoint');
        if ($endpoint === '') {
            return new \WP_REST_Response(array('ok' => false, 'error' => 'missing-endpoint'), 400);
        }
        $table = $wpdb->prefix . 'aieo_push_subscriptions';
        $rows = (int) $wpdb->update(
            $table,
            array('status' => 'revoked', 'bounced_at' => current_time('mysql', true)),
            array('endpoint_hash' => hash('sha256', $endpoint)),
            array('%s', '%s'),
            array('%s')
        );
        return new \WP_REST_Response(array('ok' => true, 'rows' => $rows), 200);
    }

    
    
    

    public function handle_event($req) {
        global $wpdb;
        $dispatch_id = (int) $req->get_param('dispatch_id');
        $kind        = (string) $req->get_param('kind');
        if ($dispatch_id <= 0 || !in_array($kind, array('click', 'dismiss'), true)) {
            return new \WP_REST_Response(array('ok' => false, 'error' => 'bad-input'), 400);
        }
        $table = $wpdb->prefix . 'aieo_push_dispatch';
        $now   = current_time('mysql', true);
        if ($kind === 'click') {
            $wpdb->update(
                $table,
                array('state' => 'clicked', 'clicked_at' => $now),
                array('dispatch_id' => $dispatch_id),
                array('%s', '%s'),
                array('%d')
            );
        } else {
            $wpdb->update(
                $table,
                array('state' => 'dismissed', 'dismissed_at' => $now),
                array('dispatch_id' => $dispatch_id),
                array('%s', '%s'),
                array('%d')
            );
        }
        return new \WP_REST_Response(array('ok' => true), 200);
    }

    
    
    

    
    private function get_or_create_session_token() {
        $cookie_name = 'aieo_push_session';
        if (isset($_COOKIE[$cookie_name]) && (string) $_COOKIE[$cookie_name] !== '') {
            $raw = preg_replace('/[^a-z0-9]/i', '', (string) wp_unslash($_COOKIE[$cookie_name]));
            if ($raw !== '') { return substr($raw, 0, 64); }
        }
        $token = wp_generate_password(32, false, false);
        if (!headers_sent()) {
            setcookie(
                $cookie_name,
                $token,
                time() + 180 * DAY_IN_SECONDS,
                COOKIEPATH ? COOKIEPATH : '/',
                COOKIE_DOMAIN
            );
        }
        $_COOKIE[$cookie_name] = $token;
        return $token;
    }

    private function guess_device_kind($ua) {
        $ua = strtolower((string) $ua);
        if ($ua === '') { return 'unknown'; }
        if (strpos($ua, 'mobile') !== false || strpos($ua, 'android') !== false || strpos($ua, 'iphone') !== false) {
            return 'mobile';
        }
        if (strpos($ua, 'ipad') !== false || strpos($ua, 'tablet') !== false) {
            return 'tablet';
        }
        return 'desktop';
    }

    private function detect_locale() {
        if (function_exists('determine_locale')) {
            return substr((string) determine_locale(), 0, 10);
        }
        if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
            $al = (string) wp_unslash($_SERVER['HTTP_ACCEPT_LANGUAGE']);
            return substr(preg_replace('/[^a-zA-Z0-9_\-]/', '', explode(',', $al)[0]), 0, 10);
        }
        return '';
    }
}
