<?php
/**
 * Kantan Analytics - REST API
 * Exposes analytics data via authenticated REST endpoints
 */

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

function kantan_register_rest_routes() {
    $namespace = 'kantan/v1';

    // Claude Code Agent: site scan
    register_rest_route($namespace, '/agent/scan', array(
        'methods'             => 'POST',
        'callback'            => 'kantan_scan_site_builders',
        'permission_callback' => 'kantan_rest_permission',
    ));

    // Claude Code Agent: generate .md file
    register_rest_route($namespace, '/agent/generate', array(
        'methods'             => 'POST',
        'callback'            => 'kantan_generate_agent',
        'permission_callback' => 'kantan_rest_permission',
        'args' => array(
            'plugin' => array(
                'required'          => true,
                'sanitize_callback' => 'sanitize_key',
            ),
            'mode' => array(
                'required'          => false,
                'default'           => 'recommend',
                'sanitize_callback' => 'sanitize_key',
            ),
        ),
    ));

    // Claude Code Agent: latest template (credential-free self-update; no app
    // password is minted, so deployed agents can refresh without rotating keys)
    register_rest_route($namespace, '/agent/latest', array(
        'methods'             => 'GET',
        'callback'            => 'kantan_agent_latest',
        'permission_callback' => 'kantan_rest_permission',
        'args' => array(
            'plugin' => array(
                'required'          => false,
                'default'           => 'analytics',
                'sanitize_callback' => 'sanitize_key',
            ),
            'mode' => array(
                'required'          => false,
                'default'           => 'recommend',
                'sanitize_callback' => 'sanitize_key',
            ),
        ),
    ));

    // Diagnostics
    register_rest_route($namespace, '/status', array(
        'methods'             => 'GET',
        'callback'            => 'kantan_rest_status',
        'permission_callback' => 'kantan_rest_admin_permission',
    ));

    // Clicks & scroll data
    register_rest_route($namespace, '/interactions', array(
        'methods'             => 'GET',
        'callback'            => 'kantan_rest_interactions',
        'permission_callback' => 'kantan_rest_permission',
        'args' => array(
            'days'     => array('default' => 30, 'sanitize_callback' => 'absint'),
            'page_url' => array('default' => '', 'sanitize_callback' => 'esc_url_raw'),
        ),
    ));

    // Click-region drill-down (what/where was clicked in one 3x3 region)
    register_rest_route($namespace, '/interactions/region', array(
        'methods'             => 'GET',
        'callback'            => 'kantan_rest_interaction_region',
        'permission_callback' => 'kantan_rest_permission',
        'args' => array(
            'region'   => array('required' => true, 'sanitize_callback' => 'absint'),
            'days'     => array('default' => 30, 'sanitize_callback' => 'absint'),
            'page_url' => array('default' => '', 'sanitize_callback' => 'esc_url_raw'),
        ),
    ));

    // Clicked-element drill-down (which pages was this control clicked on)
    register_rest_route($namespace, '/interactions/element', array(
        'methods'             => 'GET',
        'callback'            => 'kantan_rest_interaction_element',
        'permission_callback' => 'kantan_rest_permission',
        'args' => array(
            'text' => array('required' => true, 'sanitize_callback' => 'sanitize_text_field'),
            'days' => array('default' => 30, 'sanitize_callback' => 'absint'),
        ),
    ));

    // Core analytics overview
    register_rest_route($namespace, '/analytics', array(
        'methods'             => 'GET',
        'callback'            => 'kantan_rest_analytics',
        'permission_callback' => 'kantan_rest_permission',
        'args' => array(
            'days' => array('default' => 30, 'sanitize_callback' => 'absint'),
        ),
    ));

    // Traffic sources
    register_rest_route($namespace, '/traffic', array(
        'methods'             => 'GET',
        'callback'            => 'kantan_rest_traffic',
        'permission_callback' => 'kantan_rest_permission',
        'args' => array(
            'days' => array('default' => 30, 'sanitize_callback' => 'absint'),
        ),
    ));

    // Country breakdown (geo)
    register_rest_route($namespace, '/geo', array(
        'methods'             => 'GET',
        'callback'            => 'kantan_rest_geo',
        'permission_callback' => 'kantan_rest_permission',
        'args' => array(
            'days' => array('default' => 30, 'sanitize_callback' => 'absint'),
        ),
    ));

    // Full referrer list (Top Referrers "View all" modal)
    register_rest_route($namespace, '/referrers', array(
        'methods'             => 'GET',
        'callback'            => 'kantan_rest_referrers',
        'permission_callback' => 'kantan_rest_permission',
        'args' => array(
            'days'  => array('default' => 30, 'sanitize_callback' => 'absint'),
            'limit' => array('default' => 500, 'sanitize_callback' => 'absint'),
        ),
    ));

    // Referrer detail (Top Referrers click-through modal)
    register_rest_route($namespace, '/referrers/detail', array(
        'methods'             => 'GET',
        'callback'            => 'kantan_rest_referrer_detail',
        'permission_callback' => 'kantan_rest_permission',
        'args' => array(
            'days'   => array('default' => 30, 'sanitize_callback' => 'absint'),
            'domain' => array('default' => '', 'sanitize_callback' => 'sanitize_text_field'),
        ),
    ));

    // Top pages
    register_rest_route($namespace, '/pages', array(
        'methods'             => 'GET',
        'callback'            => 'kantan_rest_pages',
        'permission_callback' => 'kantan_rest_permission',
        'args' => array(
            'days'  => array('default' => 30, 'sanitize_callback' => 'absint'),
            'limit' => array('default' => 20, 'sanitize_callback' => 'absint'),
        ),
    ));

    // Conversions
    register_rest_route($namespace, '/conversions', array(
        'methods'             => 'GET',
        'callback'            => 'kantan_rest_conversions',
        'permission_callback' => 'kantan_rest_permission',
        'args' => array(
            'days'  => array('default' => 30, 'sanitize_callback' => 'absint'),
            'limit' => array('default' => 50, 'sanitize_callback' => 'absint'),
        ),
    ));

    // UTM data
    register_rest_route($namespace, '/utm', array(
        'methods'             => 'GET',
        'callback'            => 'kantan_rest_utm',
        'permission_callback' => 'kantan_rest_permission',
        'args' => array(
            'days'  => array('default' => 30, 'sanitize_callback' => 'absint'),
            'limit' => array('default' => 50, 'sanitize_callback' => 'absint'),
        ),
    ));

    // Journeys
    register_rest_route($namespace, '/journeys', array(
        'methods'             => 'GET',
        'callback'            => 'kantan_rest_journeys',
        'permission_callback' => 'kantan_rest_permission',
        'args' => array(
            'days'  => array('default' => 30, 'sanitize_callback' => 'absint'),
            'limit' => array('default' => 20, 'sanitize_callback' => 'absint'),
        ),
    ));

    // Conversion links settings
    register_rest_route($namespace, '/conversion-links', array(
        array(
            'methods'             => 'GET',
            'callback'            => 'kantan_rest_get_conversion_links',
            'permission_callback' => 'kantan_rest_admin_permission',
        ),
        array(
            'methods'             => 'PUT',
            'callback'            => 'kantan_rest_update_conversion_links',
            'permission_callback' => 'kantan_rest_permission',
        ),
    ));

    // Excluded IPs settings
    register_rest_route($namespace, '/excluded-ips', array(
        array(
            'methods'             => 'GET',
            'callback'            => 'kantan_rest_get_excluded_ips',
            'permission_callback' => 'kantan_rest_admin_permission',
        ),
        array(
            'methods'             => 'PUT',
            'callback'            => 'kantan_rest_update_excluded_ips',
            'permission_callback' => 'kantan_rest_permission',
        ),
    ));

    // Bot-filter toggle (Settings): whether human-facing metrics exclude suspected bots.
    register_rest_route($namespace, '/bot-filter', array(
        'methods'             => 'PUT',
        'callback'            => 'kantan_rest_update_bot_filter',
        'permission_callback' => 'kantan_rest_permission',
    ));

    // Visitor profiles
    register_rest_route($namespace, '/visitors', array(
        'methods'             => 'GET',
        'callback'            => 'kantan_rest_visitors',
        'permission_callback' => 'kantan_rest_permission',
        'args' => array(
            'days'  => array('default' => 30, 'sanitize_callback' => 'absint'),
            'limit' => array('default' => 20, 'sanitize_callback' => 'absint'),
        ),
    ));

    // Rename a visitor
    register_rest_route($namespace, '/visitors/(?P<visitor_id>[a-zA-Z0-9_]+)', array(
        'methods'             => 'PUT',
        'callback'            => 'kantan_rest_rename_visitor',
        'permission_callback' => 'kantan_rest_permission',
    ));

    // Single visitor history (Realtime / Profiles click-through modal)
    register_rest_route($namespace, '/visitors/(?P<visitor_id>[a-zA-Z0-9_]+)', array(
        'methods'             => 'GET',
        'callback'            => 'kantan_rest_get_visitor',
        'permission_callback' => 'kantan_rest_permission',
        'args' => array(
            'days' => array('default' => 90, 'sanitize_callback' => 'absint'),
        ),
    ));

    // User data (devices, browsers, OS, resolutions)
    register_rest_route($namespace, '/user-data', array(
        'methods'             => 'GET',
        'callback'            => 'kantan_rest_user_data',
        'permission_callback' => 'kantan_rest_permission',
        'args' => array(
            'days'  => array('default' => 30, 'sanitize_callback' => 'absint'),
            'limit' => array('default' => 10, 'sanitize_callback' => 'absint'),
        ),
    ));

    // Device breakdown
    register_rest_route($namespace, '/devices', array(
        'methods'             => 'GET',
        'callback'            => 'kantan_rest_devices',
        'permission_callback' => 'kantan_rest_permission',
        'args' => array(
            'days' => array('default' => 30, 'sanitize_callback' => 'absint'),
        ),
    ));

    // Realtime visitors (drives Realtime tab + nav badge poller)
    register_rest_route($namespace, '/realtime', array(
        'methods'             => 'GET',
        'callback'            => 'kantan_rest_realtime',
        'permission_callback' => 'kantan_rest_permission',
        'args' => array(
            'window' => array('default' => 5, 'sanitize_callback' => 'absint'),
        ),
    ));

    // Retention & storage
    register_rest_route($namespace, '/retention', array(
        array(
            'methods'             => 'GET',
            'callback'            => 'kantan_rest_get_retention',
            'permission_callback' => 'kantan_rest_admin_permission',
        ),
        array(
            'methods'             => 'PUT',
            'callback'            => 'kantan_rest_update_retention',
            'permission_callback' => 'kantan_rest_permission',
        ),
    ));

    register_rest_route($namespace, '/retention/aggregate-now', array(
        'methods'             => 'POST',
        'callback'            => 'kantan_rest_run_aggregation',
        'permission_callback' => 'kantan_rest_permission',
    ));

}
add_action('rest_api_init', 'kantan_register_rest_routes');

/**
 * Permission check — require manage_options (admin with app password)
 */
function kantan_rest_permission($request = null) {
    if (current_user_can('manage_options')) {
        return true;
    }
    // Read-only dashboard viewers (e.g. the public demo) may GET data, never write.
    if ($request instanceof WP_REST_Request && $request->get_method() === 'GET' && current_user_can('kantan_view_dashboard')) {
        return true;
    }
    return false;
}

/**
 * Stricter permission check — always require manage_options, even for GET.
 *
 * Used for diagnostics and settings/config endpoints (/status, /conversion-links,
 * /webhook/config) whose responses expose configuration that read-only dashboard
 * viewers (the kantan_view_dashboard capability, e.g. the public demo) must not see.
 */
function kantan_rest_admin_permission() {
    return current_user_can('manage_options');
}

/**
 * GET /kantan/v1/status
 */
function kantan_rest_status() {
    global $wpdb;

    $tables = array('kantan_analytics', 'kantan_clicks', 'kantan_pageviews', 'kantan_utm', 'kantan_conversions');
    $status = array(
        'version' => KANTAN_ANALYTICS_VERSION,
        'db_version' => get_option('kantan_analytics_db_version', 'not set'),
        'tables' => array(),
    );

    foreach ($tables as $table) {
        $full = $wpdb->prefix . $table;
        $exists = $wpdb->get_var("SHOW TABLES LIKE '$full'") === $full;
        $count = $exists ? (int) $wpdb->get_var("SELECT COUNT(*) FROM $full") : 0;
        $status['tables'][$table] = array('exists' => $exists, 'rows' => $count);
    }

    return rest_ensure_response($status);
}

/**
 * GET /kantan/v1/interactions
 */
function kantan_rest_interactions($request) {
    $days = $request->get_param('days');
    $page_url = $request->get_param('page_url');

    return rest_ensure_response(array(
        'period_days'       => $days,
        'total_clicks'      => kantan_get_total_clicks($days),
        'click_regions'     => kantan_get_click_regions($days),
        'top_clicked'       => kantan_get_top_clicked_elements($days, 15),
        'pages_with_clicks' => kantan_get_pages_with_clicks($days, 20),
        'avg_scroll_depth'  => kantan_get_avg_scroll_depth($days),
        'scroll_distribution' => kantan_get_scroll_distribution($page_url, $days),
        'scroll_by_page'    => kantan_get_scroll_depth_by_page($days, 10),
        'click_heatmap'     => kantan_get_click_heatmap($page_url, $days),
    ));
}

/**
 * GET /kantan/v1/interactions/region
 * Drill-down for one click region: the elements clicked there and a page rollup.
 */
function kantan_rest_interaction_region($request) {
    $region = (int) $request->get_param('region');
    if ($region < 1 || $region > 9) {
        return new WP_Error('invalid_region', 'Region must be between 1 and 9.', array('status' => 400));
    }

    $days     = $request->get_param('days');
    $page_url = $request->get_param('page_url');

    return rest_ensure_response(array_merge(
        array('period_days' => $days),
        kantan_get_click_region_detail($region, $days, $page_url)
    ));
}

/**
 * GET /kantan/v1/interactions/element
 * Per-page breakdown for one clicked label.
 */
function kantan_rest_interaction_element($request) {
    $text = $request->get_param('text');
    if ($text === '' || $text === null) {
        return new WP_Error('missing_text', 'An element label is required.', array('status' => 400));
    }

    $days = $request->get_param('days');

    return rest_ensure_response(array_merge(
        array('period_days' => $days),
        kantan_get_clicked_element_detail($text, $days)
    ));
}

/**
 * GET /kantan/v1/analytics
 */
function kantan_rest_analytics($request) {
    $days = $request->get_param('days');

    return rest_ensure_response(array(
        'period_days'     => $days,
        'total_visits'    => (int) kantan_get_total_visits($days),
        'avg_duration'    => (int) kantan_get_avg_duration($days),
        'bounce_rate'     => (float) kantan_get_bounce_rate($days),
        'traffic_sources' => kantan_get_traffic_sources($days),
        'ai_assistants'   => kantan_get_ai_assistant_breakdown($days),
        'visits_by_day'   => kantan_get_visits_by_day($days),
        'top_referrers'   => kantan_get_top_referrers($days, 10),
        'devices'         => kantan_get_device_breakdown($days),
        'visitor_types'   => kantan_get_visitor_type_breakdown($days),
        'unique_visitors' => kantan_get_unique_visitors($days),
    ));
}

/**
 * GET /kantan/v1/traffic
 */
function kantan_rest_traffic($request) {
    $days = $request->get_param('days');

    return rest_ensure_response(array(
        'period_days' => $days,
        'sources'     => kantan_get_traffic_sources($days),
        'referrers'   => kantan_get_top_referrers($days, 20),
    ));
}

/**
 * GET /kantan/v1/geo
 * Visitor country breakdown (sessions per ISO country code), bot-filtered.
 */
function kantan_rest_geo($request) {
    $days = $request->get_param('days');
    return rest_ensure_response(array(
        'period_days' => $days,
        'countries'   => kantan_get_country_breakdown($days),
    ));
}

/**
 * GET /kantan/v1/referrers
 * Full referrer list — drives the Top Referrers "View all" modal, which shows
 * everything the dashboard's top-10 block holds back.
 */
function kantan_rest_referrers($request) {
    $days  = $request->get_param('days');
    $limit = $request->get_param('limit');

    // Cap the page size: the list is rendered in one scrollable modal, and an
    // unbounded limit would let a busy site pull tens of thousands of rows.
    $limit = max(1, min($limit, 1000));

    return rest_ensure_response(array(
        'period_days' => $days,
        'total'       => kantan_get_referrer_domain_count($days),
        'referrers'   => kantan_get_top_referrer_domains($days, $limit),
    ));
}

/**
 * GET /kantan/v1/referrers/detail
 * Detail payload for one referrer domain — drives the Top Referrers modal.
 */
function kantan_rest_referrer_detail($request) {
    $days   = $request->get_param('days');
    $domain = $request->get_param('domain');

    return rest_ensure_response(kantan_get_referrer_domain_details($domain, $days));
}

/**
 * GET /kantan/v1/pages
 */
function kantan_rest_pages($request) {
    $days = $request->get_param('days');
    $limit = $request->get_param('limit');

    return rest_ensure_response(array(
        'period_days'  => $days,
        'top_pages'    => kantan_get_top_pages($days, $limit),
        'entry_pages'  => kantan_get_entry_pages($days, $limit),
        'exit_pages'   => kantan_get_exit_pages($days, $limit),
    ));
}

/**
 * GET /kantan/v1/conversions
 */
function kantan_rest_conversions($request) {
    global $wpdb;
    $days = $request->get_param('days');
    $limit = $request->get_param('limit');
    $date = gmdate('Y-m-d H:i:s', strtotime("-$days days"));

    $conversions_table = $wpdb->prefix . 'kantan_conversions';
    $analytics_table = $wpdb->prefix . 'kantan_analytics';

    // Summary by type
    $summary = $wpdb->get_results($wpdb->prepare(
        "SELECT c.conversion_type, COUNT(*) as total
        FROM $conversions_table c
        WHERE c.converted_at >= %s
        GROUP BY c.conversion_type
        ORDER BY total DESC",
        $date
    ));

    // Summary by type + traffic source
    $by_source = $wpdb->get_results($wpdb->prepare(
        "SELECT c.conversion_type, a.traffic_source, COUNT(*) as total
        FROM $conversions_table c
        INNER JOIN $analytics_table a ON c.session_id = a.session_id
        WHERE c.converted_at >= %s
        GROUP BY c.conversion_type, a.traffic_source
        ORDER BY total DESC",
        $date
    ));

    // Recent conversions — use COALESCE to recover visitor_id from the session record
    // for conversions recorded before the visitor_id column was added (pre-2.9.4)
    $recent = $wpdb->get_results($wpdb->prepare(
        "SELECT c.id, c.session_id,
                COALESCE(c.visitor_id, a.visitor_id) AS visitor_id,
                c.conversion_type, c.conversion_label, c.conversion_value,
                c.page_url, c.converted_at,
                a.traffic_source, a.referrer
        FROM $conversions_table c
        LEFT JOIN $analytics_table a ON c.session_id = a.session_id
        WHERE c.converted_at >= %s
        ORDER BY c.converted_at DESC
        LIMIT %d",
        $date, $limit
    ));

    return rest_ensure_response(array(
        'period_days' => $days,
        'summary'     => $summary,
        'by_source'   => $by_source,
        'recent'      => $recent,
    ));
}

/**
 * GET /kantan/v1/utm
 */
function kantan_rest_utm($request) {
    global $wpdb;
    $days = $request->get_param('days');
    $limit = $request->get_param('limit');
    $date = gmdate('Y-m-d H:i:s', strtotime("-$days days"));

    $utm_table = $wpdb->prefix . 'kantan_utm';

    // Campaign summary
    $campaigns = $wpdb->get_results($wpdb->prepare(
        "SELECT utm_source, utm_medium, utm_campaign, COUNT(*) as sessions
        FROM $utm_table
        WHERE created_at >= %s AND utm_source != ''
        GROUP BY utm_source, utm_medium, utm_campaign
        ORDER BY sessions DESC
        LIMIT %d",
        $date, $limit
    ));

    // Recent UTM entries
    $recent = $wpdb->get_results($wpdb->prepare(
        "SELECT * FROM $utm_table
        WHERE created_at >= %s
        ORDER BY created_at DESC
        LIMIT %d",
        $date, $limit
    ));

    return rest_ensure_response(array(
        'period_days' => $days,
        'campaigns'   => $campaigns,
        'recent'      => $recent,
    ));
}

/**
 * GET /kantan/v1/journeys
 */
function kantan_rest_journeys($request) {
    $days = $request->get_param('days');
    $limit = $request->get_param('limit');

    return rest_ensure_response(array(
        'period_days'  => $days,
        'journeys'     => kantan_get_session_journeys($days, $limit),
        'transitions'  => kantan_get_page_transitions($days, $limit),
    ));
}

/**
 * GET /kantan/v1/devices
 */
function kantan_rest_devices($request) {
    $days = $request->get_param('days');

    return rest_ensure_response(array(
        'period_days' => $days,
        'devices'     => kantan_get_device_breakdown($days),
        'by_source'   => kantan_get_device_by_source($days),
    ));
}

/**
 * GET /kantan/v1/realtime — visitors active within the last `window` minutes.
 */
function kantan_rest_realtime($request) {
    $window = intval($request->get_param('window'));
    if ($window < 1 || $window > 60) $window = 5;

    $visitors = kantan_get_realtime_visitors($window);

    foreach ($visitors as $v) {
        $v->display_name = kantan_get_visitor_display_name($v->visitor_id);
    }

    return rest_ensure_response(array(
        'count'          => count($visitors),
        'window_minutes' => $window,
        'visitors'       => $visitors,
        'timestamp'      => current_time('mysql'),
    ));
}

/**
 * GET /kantan/v1/user-data
 */
function kantan_rest_user_data($request) {
    $days  = $request->get_param('days');
    $limit = $request->get_param('limit');

    return rest_ensure_response(array(
        'period_days'  => $days,
        'devices'      => kantan_get_device_breakdown($days),
        'browsers'     => kantan_get_browser_breakdown($days, $limit),
        'os'           => kantan_get_os_breakdown($days, $limit),
        'resolutions'  => kantan_get_screen_resolutions($days, $limit),
    ));
}

/**
 * GET /kantan/v1/conversion-links
 */
function kantan_rest_get_conversion_links() {
    return rest_ensure_response(array(
        'links' => get_option('kantan_conversion_links', array()),
    ));
}

/**
 * PUT /kantan/v1/conversion-links
 *
 * Expects JSON body:
 * {
 *   "links": [
 *     { "domain": "clients.uschedule.com", "label": "Booking", "type": "booking_click", "utm": true },
 *     { "domain": "shop.example.com", "label": "Shop", "type": "shop_click", "utm": false }
 *   ]
 * }
 */
function kantan_rest_update_conversion_links($request) {
    $body = $request->get_json_params();

    if (!isset($body['links']) || !is_array($body['links'])) {
        return new WP_Error('invalid_data', 'Expected { "links": [...] }', array('status' => 400));
    }

    $sanitized = array();
    foreach ($body['links'] as $link) {
        if (empty($link['domain'])) continue;
        $sanitized[] = array(
            'domain' => sanitize_text_field($link['domain']),
            'label'  => sanitize_text_field($link['label'] ?? ''),
            'type'   => sanitize_key($link['type'] ?? 'outbound_click'),
            'utm'    => !empty($link['utm']),
        );
    }

    update_option('kantan_conversion_links', $sanitized);

    return rest_ensure_response(array(
        'updated' => true,
        'links'   => $sanitized,
    ));
}

/**
 * GET /kantan/v1/excluded-ips
 */
function kantan_rest_get_excluded_ips() {
    return rest_ensure_response(array(
        'ips' => get_option('kantan_excluded_ips', array()),
        'current_ip' => sanitize_text_field(kantan_get_client_ip()),
    ));
}

/**
 * PUT /kantan/v1/excluded-ips
 * Expects JSON body: { "ips": ["1.2.3.4", "10.0.0.0/24"] }
 */
function kantan_rest_update_excluded_ips($request) {
    $body = $request->get_json_params();

    if (!isset($body['ips']) || !is_array($body['ips'])) {
        return new WP_Error('invalid_data', 'Expected { "ips": [...] }', array('status' => 400));
    }

    $sanitized = array();
    foreach ($body['ips'] as $ip) {
        $ip = trim(sanitize_text_field($ip));
        if (empty($ip)) continue;
        // Validate IP or CIDR
        if (filter_var($ip, FILTER_VALIDATE_IP) || preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2}$/', $ip)) {
            $sanitized[] = $ip;
        }
    }

    update_option('kantan_excluded_ips', $sanitized);

    return rest_ensure_response(array(
        'updated' => true,
        'ips' => $sanitized,
    ));
}

/**
 * PUT /kantan/v1/bot-filter
 * Expects: { "enabled": true|false }. Toggles whether human-facing aggregates
 * exclude suspected-bot sessions (the read-time heuristic). Never deletes data.
 */
function kantan_rest_update_bot_filter($request) {
    $body = $request->get_json_params();
    if (!isset($body['enabled'])) {
        return new WP_Error('invalid_data', 'Expected { "enabled": true|false }', array('status' => 400));
    }
    $enabled = rest_sanitize_boolean($body['enabled']);
    update_option('kantan_filter_bots', $enabled ? 1 : 0);

    return rest_ensure_response(array(
        'updated' => true,
        'enabled' => (bool) $enabled,
    ));
}

/**
 * GET /kantan/v1/visitors
 */
function kantan_rest_visitors($request) {
    $days = $request->get_param('days');
    $limit = $request->get_param('limit');

    return rest_ensure_response(array(
        'period_days' => $days,
        'visitors' => kantan_get_visitor_profiles($days, $limit),
    ));
}

/**
 * GET /kantan/v1/visitors/{id}
 * Full visit history for one visitor — drives the click-through history modal.
 */
function kantan_rest_get_visitor($request) {
    $visitor_id = $request->get_param('visitor_id');
    $days       = $request->get_param('days') ?: 90;

    $profile = kantan_get_visitor_profile($visitor_id, $days);
    if (!$profile) {
        return new WP_Error('not_found', 'No history for this visitor in the period.', array('status' => 404));
    }

    return rest_ensure_response($profile);
}

/**
 * GET /kantan/v1/retention
 * Returns current retention settings + SQLite health snapshot.
 */
function kantan_rest_get_retention() {
    $health = function_exists('kantan_sqlite_health') ? kantan_sqlite_health() : array();
    return rest_ensure_response(array(
        'raw_retention_days' => intval(get_option('kantan_retention_raw_days', 90)),
        'sqlite'             => $health,
    ));
}

/**
 * PUT /kantan/v1/retention
 * Expects: { "raw_retention_days": 90 }
 */
function kantan_rest_update_retention($request) {
    $body = $request->get_json_params();
    if (!isset($body['raw_retention_days'])) {
        return new WP_Error('invalid_data', 'Expected { "raw_retention_days": N }', array('status' => 400));
    }
    $days = intval($body['raw_retention_days']);
    if ($days < 7 || $days > 3650) {
        return new WP_Error('invalid_range', 'raw_retention_days must be between 7 and 3650', array('status' => 400));
    }
    update_option('kantan_retention_raw_days', $days);
    return rest_ensure_response(array(
        'updated'            => true,
        'raw_retention_days' => $days,
    ));
}

/**
 * POST /kantan/v1/retention/aggregate-now
 * Manually trigger a catch-up aggregation run (useful for first-time backfill
 * after upgrading to the SQLite tier).
 */
function kantan_rest_run_aggregation() {
    if (!function_exists('kantan_analytics_run_aggregation')) {
        return new WP_Error('unavailable', 'Aggregation module not loaded', array('status' => 500));
    }
    $result = kantan_analytics_run_aggregation();
    return rest_ensure_response(array_merge(array('triggered' => true), $result));
}

/**
 * PUT /kantan/v1/visitors/{visitor_id}
 * Expects: { "name": "John Smith" }
 */
function kantan_rest_rename_visitor($request) {
    $visitor_id = $request->get_param('visitor_id');
    $body = $request->get_json_params();

    if (empty($body['name'])) {
        return new WP_Error('invalid_data', 'Expected { "name": "..." }', array('status' => 400));
    }

    kantan_set_visitor_name($visitor_id, $body['name']);

    return rest_ensure_response(array(
        'updated' => true,
        'visitor_id' => $visitor_id,
        'display_name' => kantan_get_visitor_display_name($visitor_id),
    ));
}

/**
 * POST /kantan/v1/agent/generate
 * Creates an Application Password and returns a Claude Code agent .md file.
 */
function kantan_generate_agent($request) {
    $plugin = $request->get_param('plugin');
    if (!in_array($plugin, array('analytics', 'seo'), true)) {
        return new WP_Error('invalid_plugin', 'Invalid plugin type.', array('status' => 400));
    }

    // Check SEO plugin is active if requested
    if ($plugin === 'seo' && !defined('KANTAN_SEO_VERSION')) {
        return new WP_Error('seo_not_active', 'Kantan SEO is not active.', array('status' => 400));
    }

    // Check Application Passwords are available
    if (!wp_is_application_passwords_available()) {
        return new WP_Error('app_passwords_unavailable',
            'Application Passwords are not available on this site. Check that HTTPS is enabled or that the feature is not disabled by a plugin.',
            array('status' => 400)
        );
    }

    $user = wp_get_current_user();
    $app_name = ($plugin === 'analytics') ? 'Kantan Analytics Agent' : 'Kantan SEO Agent';

    // Remove existing app password with same name to avoid stale entries
    $existing = WP_Application_Passwords::get_user_application_passwords($user->ID);
    foreach ($existing as $item) {
        if ($item['name'] === $app_name) {
            WP_Application_Passwords::delete_application_password($user->ID, $item['uuid']);
        }
    }

    // Create new Application Password
    $result = WP_Application_Passwords::create_new_application_password(
        $user->ID,
        array('name' => $app_name)
    );

    if (is_wp_error($result)) {
        return $result;
    }

    list($password, $item) = $result;

    // Build the agent .md content with mode and scan context
    $mode = $request->get_param('mode');
    if (!in_array($mode, array('recommend', 'gutenberg', 'acf'), true)) {
        $mode = 'recommend';
    }
    $scan_data = kantan_get_scan_data();
    $agent_md = kantan_build_agent_md($plugin, $mode, $scan_data);

    return rest_ensure_response(array(
        'agent_md'     => $agent_md,
        'app_password' => $password,
        'site_url'     => get_site_url(),
        'username'     => $user->user_login,
        'filename'     => "kantan-{$plugin}-agent.md",
        'mode'         => $mode,
        'version'      => kantan_agent_template_version($plugin),
    ));
}

/**
 * Current template version for an agent. Lets deployed agents tell whether the
 * server has a newer template than the copy on disk.
 */
function kantan_agent_template_version($plugin) {
    if ($plugin === 'seo') {
        return defined('KANTAN_SEO_AGENT_VERSION') ? KANTAN_SEO_AGENT_VERSION : '1.0.0';
    }
    return defined('KANTAN_AGENT_VERSION') ? KANTAN_AGENT_VERSION : '1.0.0';
}

/**
 * GET /kantan/v1/agent/latest
 * Returns the latest agent .md + its template version WITHOUT minting an
 * Application Password. This is what a deployed agent curls to self-update:
 * the agent reuses the key it already has, so updating the prompt never
 * rotates credentials.
 */
function kantan_agent_latest($request) {
    $plugin = $request->get_param('plugin');
    if (!in_array($plugin, array('analytics', 'seo'), true)) {
        $plugin = 'analytics';
    }
    if ($plugin === 'seo' && !defined('KANTAN_SEO_VERSION')) {
        return new WP_Error('seo_not_active', 'Kantan SEO is not active.', array('status' => 400));
    }

    $mode = $request->get_param('mode');
    if (!in_array($mode, array('recommend', 'gutenberg', 'acf'), true)) {
        $mode = 'recommend';
    }

    $scan_data = kantan_get_scan_data();
    $agent_md  = kantan_build_agent_md($plugin, $mode, $scan_data);

    return rest_ensure_response(array(
        'plugin'   => $plugin,
        'mode'     => $mode,
        'version'  => kantan_agent_template_version($plugin),
        'filename' => "kantan-{$plugin}-agent.md",
        'agent_md' => $agent_md,
    ));
}

/**
 * POST /kantan/v1/agent/scan
 * Scans the site for page builders and returns compatibility info.
 */
function kantan_scan_site_builders() {
    return rest_ensure_response(kantan_get_scan_data());
}

/**
 * Detect page builders and content storage methods.
 * Used by both /agent/scan and /agent/generate.
 */
function kantan_get_scan_data() {
    global $wpdb;

    $builders = array();
    $warnings = array();

    // Total published content
    $total_pages = (int) $wpdb->get_var(
        "SELECT COUNT(*) FROM {$wpdb->posts}
         WHERE post_type IN ('page','post') AND post_status = 'publish'"
    );

    $builder_page_total = 0;

    // ACF
    $acf_active = class_exists('ACF') || function_exists('acf_get_field_groups');
    $acf_data = array('active' => $acf_active, 'page_count' => 0, 'rest_enabled' => false, 'field_groups' => array());
    if ($acf_active && function_exists('acf_get_field_groups')) {
        $groups = acf_get_field_groups();
        $rest_enabled = false;
        $has_flexible = false;
        $group_info = array();

        foreach ($groups as $group) {
            $group_detail = array('title' => $group['title'], 'fields' => array());
            if (!empty($group['show_in_rest'])) $rest_enabled = true;

            if (function_exists('acf_get_fields')) {
                $fields = acf_get_fields($group['key']);
                if ($fields) {
                    foreach ($fields as $field) {
                        $group_detail['fields'][] = array(
                            'name' => $field['name'],
                            'type' => $field['type'],
                            'label' => $field['label'],
                        );
                        if ($field['type'] === 'flexible_content' || $field['type'] === 'repeater') {
                            $has_flexible = true;
                        }
                    }
                }
            }
            $group_info[] = $group_detail;
        }

        // Count posts using ACF (posts with any postmeta from ACF field names)
        $field_names = array();
        foreach ($group_info as $g) {
            foreach ($g['fields'] as $f) {
                if (!empty($f['name'])) $field_names[] = $f['name'];
            }
        }
        $acf_page_count = 0;
        if (!empty($field_names)) {
            $placeholders = implode(',', array_fill(0, count($field_names), '%s'));
            $acf_page_count = (int) $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(DISTINCT post_id) FROM {$wpdb->postmeta}
                 WHERE meta_key IN ($placeholders)
                 AND post_id IN (SELECT ID FROM {$wpdb->posts} WHERE post_status = 'publish')",
                $field_names
            ));
        }

        $acf_data['page_count'] = $acf_page_count;
        $acf_data['rest_enabled'] = $rest_enabled;
        $acf_data['field_groups'] = $group_info;
        $acf_data['has_flexible'] = $has_flexible;

        if (!$rest_enabled && $acf_page_count > 0) {
            $warnings[] = 'ACF fields are not REST-enabled. Direct Edit (ACF) mode requires enabling "Show in REST API" on your field groups.';
        }
        // ACF pages may overlap with Gutenberg, don't add to builder_page_total
    }
    $builders['acf'] = $acf_data;

    // Elementor
    $elem_active = defined('ELEMENTOR_VERSION');
    $elem_count = 0;
    if ($elem_active) {
        $elem_count = (int) $wpdb->get_var(
            "SELECT COUNT(DISTINCT post_id) FROM {$wpdb->postmeta}
             WHERE meta_key = '_elementor_data' AND meta_value != ''
             AND post_id IN (SELECT ID FROM {$wpdb->posts} WHERE post_status = 'publish')"
        );
        $builder_page_total += $elem_count;
        if ($elem_count > 0) {
            $warnings[] = 'Elementor stores content in a proprietary format. Direct content editing is not supported for Elementor pages — use Recommend Only mode for those pages.';
        }
    }
    $builders['elementor'] = array('active' => $elem_active, 'page_count' => $elem_count);

    // Beaver Builder
    $bb_active = defined('FL_BUILDER_VERSION');
    $bb_count = 0;
    if ($bb_active) {
        $bb_count = (int) $wpdb->get_var(
            "SELECT COUNT(DISTINCT post_id) FROM {$wpdb->postmeta}
             WHERE meta_key = '_fl_builder_data'
             AND post_id IN (SELECT ID FROM {$wpdb->posts} WHERE post_status = 'publish')"
        );
        $builder_page_total += $bb_count;
    }
    $builders['beaver_builder'] = array('active' => $bb_active, 'page_count' => $bb_count);

    // Divi
    $divi_active = defined('ET_BUILDER_VERSION');
    $divi_count = 0;
    if ($divi_active) {
        $divi_count = (int) $wpdb->get_var(
            "SELECT COUNT(DISTINCT post_id) FROM {$wpdb->postmeta}
             WHERE meta_key = '_et_builder_use_builder' AND meta_value = 'on'
             AND post_id IN (SELECT ID FROM {$wpdb->posts} WHERE post_status = 'publish')"
        );
        $builder_page_total += $divi_count;
    }
    $builders['divi'] = array('active' => $divi_active, 'page_count' => $divi_count);

    // WPBakery
    $wpb_active = defined('WPB_VC_VERSION');
    $wpb_count = 0;
    if ($wpb_active) {
        $wpb_count = (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$wpdb->posts}
             WHERE post_status = 'publish' AND post_type IN ('page','post')
             AND post_content LIKE '%[vc_%'"
        );
        $builder_page_total += $wpb_count;
    }
    $builders['wpbakery'] = array('active' => $wpb_active, 'page_count' => $wpb_count);

    // Gutenberg (everything not claimed by a visual builder)
    $gutenberg_count = max(0, $total_pages - $builder_page_total);
    $builders['gutenberg'] = array('active' => true, 'page_count' => $gutenberg_count);

    // Recommended mode
    $recommended = 'recommend';
    if ($acf_active && $acf_data['rest_enabled'] && $acf_data['page_count'] > $gutenberg_count) {
        $recommended = 'acf';
    } elseif ($gutenberg_count >= $total_pages * 0.5 && $builder_page_total === 0) {
        $recommended = 'gutenberg';
    }

    return array(
        'builders'         => $builders,
        'recommended_mode' => $recommended,
        'warnings'         => $warnings,
        'total_pages'      => $total_pages,
    );
}

