<?php
/**
 * Plugin Name: WPGC VideoChat (Licensed)
 * Description: WebRTC video chat with commercial licensing (one domain per license). TURN, chat log, presence. Uses WP License Server REST.
 * Version: 2.0.2
 * Requires PHP: 8.0
 * Author: Sincotechs.com
 * License: GPL-2.0-or-later
 */
if (!defined('ABSPATH')) { exit; }

class WPGC_VideoChat_Licensed {
    const VERSION = '2.0.2';
    const OPTION_RULES = 'wpgc_rules';
    const OPTION_DEFAULT_ROOM = 'wpgc_default_room';
    const OPTION_TURN_URLS = 'wpgc_turn_urls';
    const OPTION_TURN_USERNAME = 'wpgc_turn_username';
    const OPTION_TURN_CREDENTIAL = 'wpgc_turn_credential';
    const OPTION_LICENSE_KEY = 'wpgc_license_key';
    const OPTION_LICENSE_STATUS = 'wpgc_license_status'; // active|invalid|expired|unbound
    const OPTION_LICENSE_EXPIRES = 'wpgc_license_expires';
    const OPTION_LICENSE_API_BASE = 'wpgc_license_api_base'; // e.g. https://sincotechs.com
    const TRANSIENT_LICENSE_CACHE = 'wpgc_license_cache_24h';
    const TRANSIENT_ACTIVATE_ERROR = 'wpgc_activate_error';

    private static $instance = null;

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

    private function __construct() {
        register_activation_hook(__FILE__, [__CLASS__, 'on_activate_static']);
        register_deactivation_hook(__FILE__, [__CLASS__, 'on_deactivate_static']);
        add_action('init', [$this, 'register_shortcodes']);
        add_action('admin_menu', [$this, 'register_admin_menu']);
        add_action('rest_api_init', [$this, 'register_rest']);
        add_action('wp_enqueue_scripts', [$this, 'enqueue_assets']);
        add_action('admin_enqueue_scripts', [$this, 'enqueue_assets_admin']);
        add_action('wpgc_daily_license_check', [$this, 'license_check_cron']);
        add_action('admin_notices', [__CLASS__, 'maybe_show_activation_error']);
    }

    public static function maybe_show_activation_error() {
        $msg = get_transient(self::TRANSIENT_ACTIVATE_ERROR);
        if ($msg) {
            echo '<div class="notice notice-error"><p><strong>WPGC activation issue:</strong> ' . esc_html($msg) . '</p></div>';
            delete_transient(self::TRANSIENT_ACTIVATE_ERROR);
        }
    }

    public static function on_activate_static() {
        try {
            if (version_compare(PHP_VERSION, '8.0.0', '<')) {
                throw new Exception('PHP 8.0 or higher is required.');
            }
            self::create_tables_static();
            if (!wp_next_scheduled('wpgc_daily_license_check')) {
                wp_schedule_event(time() + 60, 'daily', 'wpgc_daily_license_check');
            }
            if (!get_option(self::OPTION_RULES)) add_option(self::OPTION_RULES, 'Be respectful. No spam. Follow the community rules.');
            if (!get_option(self::OPTION_DEFAULT_ROOM)) add_option(self::OPTION_DEFAULT_ROOM, 'main');
            if (!get_option(self::OPTION_LICENSE_API_BASE)) add_option(self::OPTION_LICENSE_API_BASE, 'https://sincotechs.com');
        } catch (Throwable $e) {
            set_transient(self::TRANSIENT_ACTIVATE_ERROR, $e->getMessage(), 60);
        }
    }

    public static function on_deactivate_static() { wp_clear_scheduled_hook('wpgc_daily_license_check'); }

    private static function create_tables_static() {
        global $wpdb;
        $charset_collate = $wpdb->get_charset_collate();
        $signals = $wpdb->prefix . 'wpgc_signals';
        $messages = $wpdb->prefix . 'wpgc_messages';
        if (!function_exists('dbDelta')) { require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); }
        $sql2 = "CREATE TABLE $signals (
            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
            room VARCHAR(191) NOT NULL,
            user_id BIGINT UNSIGNED NOT NULL,
            target_id BIGINT UNSIGNED NULL,
            type VARCHAR(50) NOT NULL,
            payload LONGTEXT NOT NULL,
            created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY  (id),
            KEY room_idx (room),
            KEY created_at_idx (created_at)
        ) $charset_collate;";
        dbDelta($sql2);
        $sql3 = "CREATE TABLE $messages (
            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
            room VARCHAR(191) NOT NULL,
            user_id BIGINT UNSIGNED NOT NULL,
            message TEXT NOT NULL,
            created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY  (id),
            KEY room_idx (room),
            KEY created_at_idx (created_at)
        ) $charset_collate;";
        dbDelta($sql3);
    }

    public function register_shortcodes() { add_shortcode('vc_room', [$this, 'shortcode_room']); add_shortcode('vc_admin', [$this, 'shortcode_admin']); }

    public function enqueue_assets() {
        $ver = self::VERSION;
        wp_enqueue_style('wpgc-style', plugins_url('assets/css/style.css', __FILE__), [], $ver);
        wp_enqueue_script('wpgc-room', plugins_url('assets/js/room.js', __FILE__), ['wp-api','jquery'], $ver, true);
        $turn_urls = trim(get_option(self::OPTION_TURN_URLS, ''));
        $turn = ['urls' => array_filter(array_map('trim', explode(',', $turn_urls))),'username' => trim(get_option(self::OPTION_TURN_USERNAME, '')),'credential' => trim(get_option(self::OPTION_TURN_CREDENTIAL, ''))];
        wp_localize_script('wpgc-room', 'WPGCL', [
            'rest' => esc_url_raw(rest_url('wpgc/v1/')),
            'nonce' => wp_create_nonce('wp_rest'),
            'currentUser' => get_current_user_id(),
            'displayName' => is_user_logged_in() ? wp_get_current_user()->display_name : 'Guest',
            'rules' => wp_kses_post(get_option(self::OPTION_RULES, '')),
            'defaultRoom' => sanitize_title(get_option(self::OPTION_DEFAULT_ROOM, 'main')),
            'turn' => $turn,
            'credit' => 'Powered by Sincotechs.com',
            'licenseOk' => $this->license_is_ok()
        ]);
    }

    public function enqueue_assets_admin() {
        $ver = self::VERSION;
        wp_enqueue_style('wpgc-style', plugins_url('assets/css/style.css', __FILE__), [], $ver);
        wp_enqueue_script('wpgc-admin', plugins_url('assets/js/admin.js', __FILE__), ['jquery'], $ver, true);
        wp_localize_script('wpgc-admin', 'WPGCAdmin', [
            'rest' => esc_url_raw(rest_url('wpgc/v1/')),
            'nonce' => wp_create_nonce('wp_rest'),
            'licenseStatus' => get_option(self::OPTION_LICENSE_STATUS, 'invalid'),
            'licenseExpires' => get_option(self::OPTION_LICENSE_EXPIRES, '')
        ]);
    }

    public function register_admin_menu() { add_menu_page('VideoChat', 'VideoChat', 'manage_options', 'wpgc', [$this, 'admin_page'], 'dashicons-video-alt3', 58); }

    public function admin_page() { if (!current_user_can('manage_options')) return; include __DIR__ . '/templates/admin.php'; }

    public function register_rest() {
        register_rest_route('wpgc/v1', '/signal', ['methods'=>'POST','permission_callback'=>function(){return is_user_logged_in() && $this->license_is_ok();},'callback'=>[$this,'rest_signal_post']]);
        register_rest_route('wpgc/v1', '/signal/fetch', ['methods'=>'GET','permission_callback'=>function(){return is_user_logged_in() && $this->license_is_ok();},'callback'=>[$this,'rest_signal_fetch']]);
        register_rest_route('wpgc/v1', '/presence', ['methods'=>'POST','permission_callback'=>function(){return is_user_logged_in() && $this->license_is_ok();},'callback'=>[$this,'rest_presence']]);
        register_rest_route('wpgc/v1', '/presence/list', ['methods'=>'GET','permission_callback'=>function(){return is_user_logged_in() && $this->license_is_ok();},'callback'=>[$this,'rest_presence_list']]);
        register_rest_route('wpgc/v1', '/message', ['methods'=>'POST','permission_callback'=>function(){return is_user_logged_in() && $this->license_is_ok();},'callback'=>[$this,'rest_message_post']]);
        register_rest_route('wpgc/v1', '/message/fetch', ['methods'=>'GET','permission_callback'=>function(){return is_user_logged_in() && $this->license_is_ok();},'callback'=>[$this,'rest_message_fetch']]);
        register_rest_route('wpgc/v1', '/admin/settings', ['methods'=>'POST','permission_callback'=>function(){return current_user_can('manage_options');},'callback'=>[$this,'rest_admin_settings']]);
        register_rest_route('wpgc/v1', '/admin/license', ['methods'=>'POST','permission_callback'=>function(){return current_user_can('manage_options');},'callback'=>[$this,'rest_admin_license']]);
    }

    private function license_is_ok() : bool {
        $cache = get_transient(self::TRANSIENT_LICENSE_CACHE);
        if (is_array($cache) && !empty($cache['ok'])) return true;
        $status = get_option(self::OPTION_LICENSE_STATUS, 'invalid');
        if (in_array($status, ['active'])) return true;
        return false;
    }

    public function license_check_cron() { $this->validate_license_with_server(); }

    private function build_verify_urls($key, $domain){
        $base = rtrim(get_option(self::OPTION_LICENSE_API_BASE, 'https://sincotechs.com'), '/');
        $rest = $base . '/wp-json/sincotechs/v1/license-verify?key=' . urlencode($key) . '&domain=' . urlencode($domain);
        $fallback = $base . '/?wpgc_license_verify=1&key=' . urlencode($key) . '&domain=' . urlencode($domain);
        return [$rest, $fallback];
    }

    private function validate_license_with_server() {
        $key = trim(get_option(self::OPTION_LICENSE_KEY, ''));
        if ($key === '') {
            update_option(self::OPTION_LICENSE_STATUS, 'invalid');
            delete_transient(self::TRANSIENT_LICENSE_CACHE);
            return ['ok'=>false, 'message'=>'No license key'];
        }
        $domain = $_SERVER['SERVER_NAME'] ?? parse_url(home_url(), PHP_URL_HOST);
        list($url1, $url2) = $this->build_verify_urls($key, $domain);

        foreach ([$url1, $url2] as $url) {
            $res = wp_remote_get($url, ['timeout'=>10]);
            if (is_wp_error($res)) { continue; }
            $body = wp_remote_retrieve_body($res);
            $data = json_decode($body, true);
            if (!is_array($data)) { continue; }
            if (!empty($data['valid'])) {
                update_option(self::OPTION_LICENSE_STATUS, 'active');
                update_option(self::OPTION_LICENSE_EXPIRES, sanitize_text_field($data['expires'] ?? ''));
                set_transient(self::TRANSIENT_LICENSE_CACHE, ['ok'=>true], DAY_IN_SECONDS);
                return ['ok'=>true];
            } else {
                $status = (!empty($data['reason']) && $data['reason']==='expired') ? 'expired' : 'invalid';
                update_option(self::OPTION_LICENSE_STATUS, $status);
                delete_transient(self::TRANSIENT_LICENSE_CACHE);
                return ['ok'=>false, 'message'=>$data['message'] ?? 'Invalid license'];
            }
        }
        return ['ok'=>false, 'message'=>'License server unreachable'];
    }

    public function shortcode_room($atts = []) {
        if (!is_user_logged_in()) { return '<p>Please <a href="'.esc_url(wp_login_url()).'">log in</a> to join the video room.</p>'; }
        if (!$this->license_is_ok()) { return '<div class="wpgc-card"><strong>License required</strong><br>Please enter and activate your license in <em>VideoChat → Settings</em>.</div>'; }
        $atts = shortcode_atts(['room'=>get_option(self::OPTION_DEFAULT_ROOM, 'main')], $atts);
        ob_start(); include __DIR__ . '/templates/room.php'; return ob_get_clean();
    }

    public function shortcode_admin() { if (!current_user_can('manage_options')) return '<p>Insufficient permissions.</p>'; ob_start(); include __DIR__ . '/templates/admin.php'; return ob_get_clean(); }

    // ---- REST handlers ----
    public function rest_signal_post(\WP_REST_Request $req) {
        global $wpdb; $p=$req->get_json_params();
        $room=sanitize_text_field($p['room']??'main'); $type=sanitize_text_field($p['type']??''); $payload=wp_json_encode($p['payload']??[]); $target=isset($p['target_id'])?intval($p['target_id']):null;
        $t=$wpdb->prefix.'wpgc_signals'; $wpdb->insert($t,['room'=>$room,'user_id'=>get_current_user_id(),'target_id'=>$target,'type'=>$type,'payload'=>$payload]);
        return ['ok'=>true,'id'=>$wpdb->insert_id];
    }
    public function rest_signal_fetch(\WP_REST_Request $req) {
        global $wpdb; $room=sanitize_text_field($req->get_param('room')??'main'); $since=sanitize_text_field($req->get_param('since')??'1970-01-01 00:00:00'); $me=get_current_user_id();
        $t=$wpdb->prefix.'wpgc_signals';
        $rows=$wpdb->get_results($wpdb->prepare("SELECT * FROM $t WHERE room=%s AND created_at >= %s AND (target_id IS NULL OR target_id=%d) AND user_id<>%d ORDER BY id ASC LIMIT 200",$room,$since,$me,$me),ARRAY_A);
        return ['signals'=>$rows,'now'=>current_time('mysql')];
    }
    public function rest_presence(\WP_REST_Request $req) {
        $p=$req->get_json_params(); $room=sanitize_text_field($p['room']??'main'); $key='wpgc_presence_'.sanitize_key($room); $list=get_transient($key); if(!is_array($list))$list=[];
        $uid=get_current_user_id(); $list[$uid]=['user_id'=>$uid,'display'=>wp_get_current_user()->display_name,'ts'=>time()];
        set_transient($key,$list,60); return ['ok'=>true];
    }
    public function rest_presence_list(\WP_REST_Request $req) {
        $room=sanitize_text_field($req->get_param('room')??'main'); $key='wpgc_presence_'.sanitize_key($room); $list=get_transient($key); if(!is_array($list))$list=[];
        $now=time(); $list=array_filter($list,function($u)use($now){return ($now-($u['ts']??0))<75;}); set_transient($key,$list,60); return array_values($list);
    }
    public function rest_message_post(\WP_REST_Request $req) {
        global $wpdb; $p=$req->get_json_params(); $room=sanitize_text_field($p['room']??'main'); $msg=wp_kses_post($p['message']??''); if(''===trim($msg)) return new \WP_Error('empty','Empty message',['status'=>400]);
        $t=$wpdb->prefix.'wpgc_messages'; $wpdb->insert($t,['room'=>$room,'user_id'=>get_current_user_id(),'message'=>$msg]); return ['ok'=>true];
    }
    public function rest_message_fetch(\WP_REST_Request $req) {
        global $wpdb; $room=sanitize_text_field($req->get_param('room')??'main'); $since_id=intval($req->get_param('since_id')??0); $limit=100;
        $t=$wpdb->prefix.'wpgc_messages'; $rows=$wpdb->get_results($wpdb->prepare("SELECT m.id,m.message,m.created_at,u.display_name FROM $t m LEFT JOIN {$wpdb->users} u ON u.ID=m.user_id WHERE m.room=%s AND m.id>%d ORDER BY m.id ASC LIMIT %d",$room,$since_id,$limit),ARRAY_A);
        return ['messages'=>$rows];
    }
    public function rest_admin_settings(\WP_REST_Request $req) {
        $p=$req->get_json_params();
        if(isset($p['rules'])) update_option(self::OPTION_RULES, wp_kses_post($p['rules']));
        if(isset($p['default_room'])) update_option(self::OPTION_DEFAULT_ROOM, sanitize_title($p['default_room']));
        if(isset($p['turn_urls'])) update_option(self::OPTION_TURN_URLS, sanitize_text_field($p['turn_urls']));
        if(isset($p['turn_username'])) update_option(self::OPTION_TURN_USERNAME, sanitize_text_field($p['turn_username']));
        if(isset($p['turn_credential'])) update_option(self::OPTION_TURN_CREDENTIAL, sanitize_text_field($p['turn_credential']));
        if(isset($p['license_api_base'])) update_option(self::OPTION_LICENSE_API_BASE, esc_url_raw($p['license_api_base']));
        return ['ok'=>true];
    }
    public function rest_admin_license(\WP_REST_Request $req) {
        $p=$req->get_json_params(); $key=sanitize_text_field($p['license_key']??''); if($key==='') return new \WP_Error('empty','License key required',['status'=>400]);
        update_option(self::OPTION_LICENSE_KEY,$key); $rs=$this->validate_license_with_server();
        if(!empty($rs['ok'])) { return ['ok'=>true,'status'=>'active','expires'=>get_option(self::OPTION_LICENSE_EXPIRES,'')]; }
        else { return new \WP_Error('invalid','License not valid: '.($rs['message']??''),['status'=>400]); }
    }
}
WPGC_VideoChat_Licensed::instance();
