<?php
if ( ! defined( 'ABSPATH' ) ) exit;

/**
 * REST API: /vlens/v1/replay/*
 * Serves session lists, events, delete, and clear operations.
 */
class VLENS_REST_Replay {

    public function register_routes() {
        $admin_perm = function () {
            return current_user_can( 'manage_options' );
        };

        register_rest_route( 'vlens/v1', '/replay/sessions', [
            'methods'             => 'GET',
            'callback'            => [ $this, 'get_sessions' ],
            'permission_callback' => $admin_perm,
        ] );

        register_rest_route( 'vlens/v1', '/replay/events/(?P<id>\d+)', [
            'methods'             => 'GET',
            'callback'            => [ $this, 'get_events' ],
            'permission_callback' => $admin_perm,
        ] );

        register_rest_route( 'vlens/v1', '/replay/delete-ip', [
            'methods'             => 'DELETE',
            'callback'            => [ $this, 'delete_by_ip' ],
            'permission_callback' => $admin_perm,
        ] );

        register_rest_route( 'vlens/v1', '/replay/clear', [
            'methods'             => 'DELETE',
            'callback'            => [ $this, 'clear_all' ],
            'permission_callback' => $admin_perm,
        ] );

        // 19a: Export sessions as CSV or JSON
        register_rest_route( 'vlens/v1', '/replay/export', [
            'methods'             => 'GET',
            'callback'            => [ $this, 'export_sessions' ],
            'permission_callback' => $admin_perm,
        ] );

        // 19c: Bulk-delete sessions matching current filter
        register_rest_route( 'vlens/v1', '/replay/bulk-delete', [
            'methods'             => 'DELETE',
            'callback'            => [ $this, 'bulk_delete' ],
            'permission_callback' => $admin_perm,
        ] );
    }

    public function get_sessions( WP_REST_Request $request ) {
        global $wpdb;

        $t_sessions = VLENS_Helpers::table( 'sessions' );
        $t_visitors = VLENS_Helpers::table( 'visitors' );
        $t_online   = VLENS_Helpers::table( 'online' );

        $page       = max( 1, absint( $request->get_param( 'page' ) ?: 1 ) );
        $limit      = 30;
        $filter_uri = sanitize_text_field( $request->get_param( 'uri' ) ?: '' );
        $date_from  = sanitize_text_field( $request->get_param( 'date_from' ) ?: '' );
        $date_to    = sanitize_text_field( $request->get_param( 'date_to' ) ?: '' );
        $device     = sanitize_text_field( $request->get_param( 'device' ) ?: '' );

        $conditions = [];
        if ( $filter_uri ) {
            $conditions[] = $wpdb->prepare( "s.page_uri LIKE %s", '%' . $wpdb->esc_like( $filter_uri ) . '%' );
        }
        if ( $date_from ) {
            $conditions[] = $wpdb->prepare( "s.started_at >= %s", $date_from . ' 00:00:00' );
        }
        if ( $date_to ) {
            $conditions[] = $wpdb->prepare( "s.started_at <= %s", $date_to . ' 23:59:59' );
        }
        if ( $device ) {
            switch ( $device ) {
                case 'mobile':  $conditions[] = "s.viewport_w > 0 AND s.viewport_w < 768"; break;
                case 'tablet':  $conditions[] = "s.viewport_w >= 768 AND s.viewport_w < 1024"; break;
                case 'desktop': $conditions[] = "s.viewport_w >= 1024"; break;
            }
        }

        // Premium can add extra WHERE conditions and JOINs
        $conditions  = apply_filters( 'vlens_replay_sessions_where', $conditions, $request );
        $extra_joins = apply_filters( 'vlens_replay_sessions_join', '', $request );

        $where = $conditions ? 'WHERE ' . implode( ' AND ', $conditions ) : '';

        // $where/$extra_joins are built solely from wpdb->prepare()'d fragments (or static
        // literals), so any literal "%" they contain must be escaped to "%%" before the whole
        // query is run through prepare() again below (for the %i table identifiers) — otherwise
        // prepare() would try to re-interpret those already-escaped values as placeholders.
        $where_esc = str_replace( '%', '%%', $where );
        $joins_esc = str_replace( '%', '%%', $extra_joins );

        // Get grouped IPs sorted by last activity.
        // COALESCE(NULLIF(v.ip_raw,''),'Unknown') treats both NULL and empty-string as 'Unknown'
        // so the grouping key always matches what JS produces with `ip_raw || 'Unknown'`.
        $ip_rows = $wpdb->get_results( $wpdb->prepare(
            "SELECT COALESCE(NULLIF(v.ip_raw, ''), 'Unknown') AS ip_raw,
                    MAX(COALESCE(UNIX_TIMESTAMP(s.ended_at), UNIX_TIMESTAMP(s.started_at) + COALESCE(s.duration_ms,0)/1000)) AS last_activity_ts,
                    COUNT(DISTINCT s.id) AS session_count
             FROM %i s
             LEFT JOIN %i v ON s.visitor_id = v.id
             {$joins_esc}
             {$where_esc}
             GROUP BY COALESCE(NULLIF(v.ip_raw, ''), 'Unknown')
             ORDER BY last_activity_ts DESC",
            $t_sessions, $t_visitors
        ), ARRAY_A );
        if ( ! is_array( $ip_rows ) ) {
            $ip_rows = [];
        }

        $total_ips = count( $ip_rows );
        $offset    = ( $page - 1 ) * $limit;
        $page_ips  = array_slice( $ip_rows, $offset, $limit );
        $ip_order  = array_column( $page_ips, 'ip_raw' );

        // Fetch sessions for these IPs
        $rows = [];
        if ( $ip_order ) {
            $placeholders = implode( ',', array_fill( 0, count( $ip_order ), '%s' ) );
            $sql = "SELECT s.id, s.session_key, s.date, s.page_uri, s.page_title,
                           s.viewport_w, s.viewport_h, s.duration_ms, s.max_scroll_pct,
                           s.click_count, s.scroll_count, s.input_count, s.rage_clicks,
                           s.is_complete, s.started_at, s.ended_at,
                           COALESCE(NULLIF(v.ip_raw, ''), 'Unknown') AS ip_raw,
                           v.country_code, v.browser, v.platform,
                           v.wp_user_id,
                           u.display_name AS wp_display_name
                    FROM %i s
                    LEFT JOIN %i v ON s.visitor_id = v.id
                    LEFT JOIN %i u ON v.wp_user_id = u.ID
                    {$joins_esc}
                    WHERE COALESCE(NULLIF(v.ip_raw, ''), 'Unknown') IN ({$placeholders})";

            // Re-apply all non-IP conditions to the session detail query
            foreach ( $conditions as $cond ) {
                $sql .= " AND " . str_replace( '%', '%%', $cond );
            }
            $sql .= " ORDER BY s.started_at DESC";

            $rows = $wpdb->get_results( $wpdb->prepare( $sql, $t_sessions, $t_visitors, $wpdb->users, ...$ip_order ), ARRAY_A );
            if ( ! is_array( $rows ) ) {
                $rows = [];
            }
        }

        // Online IPs
        $online_ips = $wpdb->get_col( $wpdb->prepare(
            "SELECT ip_raw FROM %i
             WHERE ip_raw IS NOT NULL AND ip_raw != ''
             AND last_seen >= DATE_SUB(NOW(), INTERVAL 5 MINUTE)",
            $t_online
        ) );
        if ( ! is_array( $online_ips ) ) {
            $online_ips = [];
        }

        $total_sessions = (int) $wpdb->get_var( $wpdb->prepare(
            "SELECT COUNT(*) FROM %i s
             LEFT JOIN %i v ON s.visitor_id = v.id
             {$joins_esc}
             {$where_esc}",
            $t_sessions, $t_visitors
        ) );

        // Bot count: visitors flagged as bots matching the same date range
        $bot_conditions = [];
        if ( $date_from ) {
            $bot_conditions[] = $wpdb->prepare( "v.date >= %s", $date_from );
        }
        if ( $date_to ) {
            $bot_conditions[] = $wpdb->prepare( "v.date <= %s", $date_to );
        }
        $bot_where  = $bot_conditions ? 'AND ' . implode( ' AND ', $bot_conditions ) : '';
        $bot_where_esc = str_replace( '%', '%%', $bot_where );
        $bot_count  = (int) $wpdb->get_var( $wpdb->prepare(
            "SELECT COUNT(*) FROM %i v WHERE v.is_bot = 1 {$bot_where_esc}",
            $t_visitors
        ) );

        // Premium can enrich session list data
        $rows = apply_filters( 'vlens_session_list_data', $rows );

        return new WP_REST_Response( [
            'rows'       => $rows,
            'total'      => $total_sessions,
            'total_ips'  => $total_ips,
            'page'       => $page,
            'limit'      => $limit,
            'ip_order'   => $ip_order,
            'online_ips' => $online_ips,
            'bot_count'  => $bot_count,
        ] );
    }

    public function get_events( WP_REST_Request $request ) {
        global $wpdb;

        $sid = absint( $request->get_param( 'id' ) );
        if ( ! $sid ) {
            return new WP_REST_Response( [], 200 );
        }

        $t_sessions = VLENS_Helpers::table( 'sessions' );
        $t_events   = VLENS_Helpers::table( 'session_events' );

        $session = $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM %i WHERE id = %d LIMIT 1", $t_sessions, $sid
        ), ARRAY_A );

        $events = $wpdb->get_results( $wpdb->prepare(
            "SELECT t, type, x, y, scroll_x, scroll_y,
                    tag, el_id, el_class, el_text, href, input_type, input_name,
                    vp_w, vp_h, meta
             FROM %i
             WHERE session_id = %d
             ORDER BY t ASC", $t_events, $sid
        ), ARRAY_A );

        return new WP_REST_Response( [
            'session' => $session,
            'events'  => $events,
        ] );
    }

    public function delete_by_ip( WP_REST_Request $request ) {
        global $wpdb;

        $ip = sanitize_text_field( $request->get_param( 'ip' ) );
        if ( ! $ip ) {
            return new WP_REST_Response( [ 'error' => 'missing ip' ], 400 );
        }

        $t_sessions = VLENS_Helpers::table( 'sessions' );
        $t_events   = VLENS_Helpers::table( 'session_events' );
        $t_visitors = VLENS_Helpers::table( 'visitors' );

        $ids = $wpdb->get_col( $wpdb->prepare(
            "SELECT s.id FROM %i s
             LEFT JOIN %i v ON s.visitor_id = v.id
             WHERE v.ip_raw = %s", $t_sessions, $t_visitors, $ip
        ) );

        if ( $ids ) {
            $placeholders = implode( ',', array_fill( 0, count( $ids ), '%d' ) );
            $wpdb->query( $wpdb->prepare(
                "DELETE FROM %i WHERE session_id IN ({$placeholders})", $t_events, ...$ids
            ) );
            $wpdb->query( $wpdb->prepare(
                "DELETE FROM %i WHERE id IN ({$placeholders})", $t_sessions, ...$ids
            ) );

            // Fire hook for each deleted session
            foreach ( $ids as $sid ) {
                do_action( 'vlens_session_deleted', (int) $sid );
            }
        }

        return new WP_REST_Response( [ 'ok' => true, 'deleted' => count( $ids ) ] );
    }

    public function clear_all() {
        global $wpdb;

        $wpdb->query( $wpdb->prepare( 'TRUNCATE TABLE %i', VLENS_Helpers::table( 'session_events' ) ) );
        $wpdb->query( $wpdb->prepare( 'TRUNCATE TABLE %i', VLENS_Helpers::table( 'sessions' ) ) );

        return new WP_REST_Response( [ 'ok' => true ] );
    }

    /**
     * 19a — Export sessions as CSV or JSON file download.
     * Accepts same filter params as get_sessions(). Max 10,000 rows.
     * Streams the file directly and exits.
     */
    public function export_sessions( WP_REST_Request $request ) {
        global $wpdb;

        $format     = in_array( $request->get_param( 'format' ), [ 'json', 'csv' ], true )
                        ? $request->get_param( 'format' ) : 'csv';
        $filter_uri = sanitize_text_field( $request->get_param( 'uri' )       ?: '' );
        $date_from  = sanitize_text_field( $request->get_param( 'date_from' ) ?: '' );
        $date_to    = sanitize_text_field( $request->get_param( 'date_to' )   ?: '' );
        $device     = sanitize_text_field( $request->get_param( 'device' )    ?: '' );

        $t_sessions = VLENS_Helpers::table( 'sessions' );
        $t_visitors = VLENS_Helpers::table( 'visitors' );

        $conditions = [];
        if ( $filter_uri ) {
            $conditions[] = $wpdb->prepare( "s.page_uri LIKE %s", '%' . $wpdb->esc_like( $filter_uri ) . '%' );
        }
        if ( $date_from ) {
            $conditions[] = $wpdb->prepare( "s.started_at >= %s", $date_from . ' 00:00:00' );
        }
        if ( $date_to ) {
            $conditions[] = $wpdb->prepare( "s.started_at <= %s", $date_to . ' 23:59:59' );
        }
        if ( $device ) {
            switch ( $device ) {
                case 'mobile':  $conditions[] = "s.viewport_w > 0 AND s.viewport_w < 768"; break;
                case 'tablet':  $conditions[] = "s.viewport_w >= 768 AND s.viewport_w < 1024"; break;
                case 'desktop': $conditions[] = "s.viewport_w >= 1024"; break;
            }
        }

        $conditions  = apply_filters( 'vlens_replay_sessions_where', $conditions, $request );
        $extra_joins = apply_filters( 'vlens_replay_sessions_join', '', $request );
        $where       = $conditions ? 'WHERE ' . implode( ' AND ', $conditions ) : '';
        $where_esc   = str_replace( '%', '%%', $where );
        $joins_esc   = str_replace( '%', '%%', $extra_joins );

        $rows = $wpdb->get_results( $wpdb->prepare(
            "SELECT s.id, s.date, s.page_uri, s.page_title,
                    s.viewport_w, s.viewport_h, s.duration_ms, s.max_scroll_pct,
                    s.click_count, s.scroll_count, s.input_count, s.rage_clicks,
                    s.is_complete, s.started_at, s.ended_at,
                    COALESCE(NULLIF(v.ip_raw,''),'Unknown') AS ip_raw,
                    v.country_code, v.browser, v.platform
             FROM %i s
             LEFT JOIN %i v ON s.visitor_id = v.id
             {$joins_esc}
             {$where_esc}
             ORDER BY s.started_at DESC
             LIMIT 10000",
            $t_sessions, $t_visitors
        ), ARRAY_A );
        if ( ! is_array( $rows ) ) {
            $rows = [];
        }

        $date_slug = gmdate( 'Y-m-d' );

        if ( $format === 'json' ) {
            header( 'Content-Type: application/json; charset=utf-8' );
            header( 'Content-Disposition: attachment; filename="vlens-sessions-' . $date_slug . '.json"' );
            header( 'X-Robots-Tag: noindex' );
            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
            echo wp_json_encode( $rows, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE );
            exit;
        }

        // CSV (default)
        header( 'Content-Type: text/csv; charset=utf-8' );
        header( 'Content-Disposition: attachment; filename="vlens-sessions-' . $date_slug . '.csv"' );
        header( 'X-Robots-Tag: noindex' );

        $out = fopen( 'php://output', 'w' );
        fputcsv( $out, [
            'ID', 'Date', 'Page URI', 'Page Title', 'IP', 'Country',
            'Browser', 'Platform', 'Viewport W', 'Viewport H',
            'Duration (ms)', 'Max Scroll %', 'Clicks', 'Scrolls', 'Inputs',
            'Rage Clicks', 'Complete', 'Started At', 'Ended At',
        ] );
        foreach ( $rows as $r ) {
            fputcsv( $out, [
                $r['id'], $r['date'], $r['page_uri'], $r['page_title'],
                $r['ip_raw'], $r['country_code'], $r['browser'], $r['platform'],
                $r['viewport_w'], $r['viewport_h'], $r['duration_ms'],
                $r['max_scroll_pct'], $r['click_count'], $r['scroll_count'],
                $r['input_count'], $r['rage_clicks'], $r['is_complete'],
                $r['started_at'], $r['ended_at'],
            ] );
        }
        fclose( $out );
        exit;
    }

    /**
     * 19c — Bulk-delete sessions matching the same filter params as get_sessions().
     * At least one filter must be present to prevent accidental full-wipe
     * (use DELETE /replay/clear for that).
     */
    public function bulk_delete( WP_REST_Request $request ) {
        global $wpdb;

        $filter_uri = sanitize_text_field( $request->get_param( 'uri' )       ?: '' );
        $date_from  = sanitize_text_field( $request->get_param( 'date_from' ) ?: '' );
        $date_to    = sanitize_text_field( $request->get_param( 'date_to' )   ?: '' );
        $device     = sanitize_text_field( $request->get_param( 'device' )    ?: '' );

        $t_sessions = VLENS_Helpers::table( 'sessions' );
        $t_events   = VLENS_Helpers::table( 'session_events' );
        $t_visitors = VLENS_Helpers::table( 'visitors' );

        $conditions = [];
        if ( $filter_uri ) {
            $conditions[] = $wpdb->prepare( "s.page_uri LIKE %s", '%' . $wpdb->esc_like( $filter_uri ) . '%' );
        }
        if ( $date_from ) {
            $conditions[] = $wpdb->prepare( "s.started_at >= %s", $date_from . ' 00:00:00' );
        }
        if ( $date_to ) {
            $conditions[] = $wpdb->prepare( "s.started_at <= %s", $date_to . ' 23:59:59' );
        }
        if ( $device ) {
            switch ( $device ) {
                case 'mobile':  $conditions[] = "s.viewport_w > 0 AND s.viewport_w < 768"; break;
                case 'tablet':  $conditions[] = "s.viewport_w >= 768 AND s.viewport_w < 1024"; break;
                case 'desktop': $conditions[] = "s.viewport_w >= 1024"; break;
            }
        }

        $conditions  = apply_filters( 'vlens_replay_sessions_where', $conditions, $request );
        $extra_joins = apply_filters( 'vlens_replay_sessions_join', '', $request );

        if ( ! $conditions ) {
            return new WP_REST_Response( [
                'error' => 'No filters specified. Use DELETE /replay/clear to wipe all sessions.',
            ], 400 );
        }

        $where     = 'WHERE ' . implode( ' AND ', $conditions );
        $where_esc = str_replace( '%', '%%', $where );
        $joins_esc = str_replace( '%', '%%', $extra_joins );

        $ids = $wpdb->get_col( $wpdb->prepare(
            "SELECT s.id FROM %i s
             LEFT JOIN %i v ON s.visitor_id = v.id
             {$joins_esc}
             {$where_esc}",
            $t_sessions, $t_visitors
        ) );

        if ( ! $ids || ! is_array( $ids ) ) {
            return new WP_REST_Response( [ 'ok' => true, 'deleted' => 0 ] );
        }

        $placeholders = implode( ',', array_fill( 0, count( $ids ), '%d' ) );
        $wpdb->query( $wpdb->prepare(
            "DELETE FROM %i WHERE session_id IN ({$placeholders})", $t_events, ...$ids
        ) );
        $wpdb->query( $wpdb->prepare(
            "DELETE FROM %i WHERE id IN ({$placeholders})", $t_sessions, ...$ids
        ) );

        foreach ( $ids as $sid ) {
            do_action( 'vlens_session_deleted', (int) $sid );
        }

        return new WP_REST_Response( [ 'ok' => true, 'deleted' => count( $ids ) ] );
    }
}
