<?php
/**
 * Plugin Name: GoPDFTools Form Leads Dashboard
 * Plugin URI: https://gopdftools.io/plugins/gopdftools-form-leads-dashboard
 * Description: Capture leads from WPForms, Contact Form 7, and Elementor Forms into a CRM-style dashboard inside WordPress.
 * Version: 1.1.2
 * Author: GoPDFTools
 * Author URI: https://gopdftools.io
 * License: GPL v2 or later
 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain: gopdftools-form-leads-dashboard
 * Domain Path: /languages
 */

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

if (!class_exists('GPFLD_Form_Leads_Dashboard')) {
    class GPFLD_Form_Leads_Dashboard {
        const VERSION = '1.1.2';
        const SCHEMA_VERSION = '1.1.2';
        const TABLE = 'gpfld_leads';
        const CAP = 'manage_options';
        const NONCE_ACTION = 'gpfld_admin_action';
        const MENU_SLUG = 'gpfld-leads-dashboard';

        public function __construct() {
            register_activation_hook(__FILE__, [$this, 'activate']);

            add_action('admin_menu', [$this, 'register_admin_menu']);
            add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']);
            add_action('admin_init', [$this, 'maybe_upgrade_schema']);

            add_action('admin_post_gpfld_update_lead', [$this, 'handle_update_lead']);
            add_action('admin_post_gpfld_delete_lead', [$this, 'handle_delete_lead']);
            add_action('admin_post_gpfld_export_csv', [$this, 'handle_export_csv']);

            // Register hooks unconditionally.
            add_action('wpforms_process_complete', [$this, 'capture_wpforms'], 10, 4);
            add_action('wpcf7_before_send_mail', [$this, 'capture_cf7'], 10, 1);
            add_action('elementor_pro/forms/new_record', [$this, 'capture_elementor'], 10, 2);
        }

        public function activate() {
            global $wpdb;

            $table = $this->table_name();
            $charset_collate = $wpdb->get_charset_collate();

            require_once ABSPATH . 'wp-admin/includes/upgrade.php';

            $sql = "CREATE TABLE {$table} (
                id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
                source_plugin VARCHAR(60) NOT NULL DEFAULT '',
                form_id VARCHAR(100) NOT NULL DEFAULT '',
                form_name VARCHAR(255) NOT NULL DEFAULT '',
                page_id BIGINT UNSIGNED NULL,
                page_url TEXT NULL,
                referrer_url TEXT NULL,
                lead_name VARCHAR(255) NOT NULL DEFAULT '',
                lead_email VARCHAR(255) NOT NULL DEFAULT '',
                lead_phone VARCHAR(100) NOT NULL DEFAULT '',
                lead_subject VARCHAR(255) NOT NULL DEFAULT '',
                lead_message LONGTEXT NULL,
                ip_address VARCHAR(45) NOT NULL DEFAULT '',
                location VARCHAR(255) NOT NULL DEFAULT '',
                raw_fields LONGTEXT NULL,
                status VARCHAR(50) NOT NULL DEFAULT 'new',
                notes LONGTEXT NULL,
                created_at DATETIME NOT NULL,
                updated_at DATETIME NULL,
                PRIMARY KEY (id),
                KEY source_plugin (source_plugin),
                KEY status (status),
                KEY form_name (form_name(191)),
                KEY lead_email (lead_email(191)),
                KEY created_at (created_at)
            ) {$charset_collate};";

            dbDelta($sql);
            update_option('gpfld_schema_version', self::SCHEMA_VERSION);
        }

        public function maybe_upgrade_schema() {
            if (get_option('gpfld_schema_version') !== self::SCHEMA_VERSION) {
                $this->activate();
            }
        }

        private function table_name() {
            global $wpdb;
            return $wpdb->prefix . self::TABLE;
        }

        private function statuses() {
            return [
                'new' => 'New',
                'contacted' => 'Contacted',
                'qualified' => 'Qualified',
                'proposal_sent' => 'Proposal Sent',
                'converted' => 'Converted',
                'lost' => 'Lost',
            ];
        }

        private function get_param($key, $default = '') {
            return isset($_REQUEST[$key]) ? sanitize_text_field(wp_unslash($_REQUEST[$key])) : $default;
        }

        public function register_admin_menu() {
            add_menu_page(
                __('Smart Leads', 'gopdftools-form-leads-dashboard'),
                __('Smart Leads', 'gopdftools-form-leads-dashboard'),
                self::CAP,
                self::MENU_SLUG,
                [$this, 'render_dashboard_page'],
                'dashicons-groups',
                26
            );

            add_submenu_page(
                self::MENU_SLUG,
                __('Leads Dashboard', 'gopdftools-form-leads-dashboard'),
                __('Dashboard', 'gopdftools-form-leads-dashboard'),
                self::CAP,
                self::MENU_SLUG,
                [$this, 'render_dashboard_page']
            );
        }

        public function enqueue_admin_assets($hook) {
            if (strpos($hook, self::MENU_SLUG) === false) {
                return;
            }

            wp_register_style('gpfld-admin-style', false, [], self::VERSION);
            wp_enqueue_style('gpfld-admin-style');

            $css = '
                .gpfld-wrap{margin:20px 20px 0 0;font-family:Inter,system-ui,-apple-system,Segoe UI,Roboto,sans-serif}
                .gpfld-header{display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:18px}
                .gpfld-title h1{margin:0 0 6px;font-size:28px;line-height:1.2}
                .gpfld-title p{margin:0;color:#64748b;font-size:14px}
                .gpfld-actions{display:flex;gap:10px;flex-wrap:wrap}
                .gpfld-btn,.gpfld-btn-secondary,.gpfld-btn-disabled{display:inline-flex;align-items:center;justify-content:center;border-radius:10px;padding:10px 14px;text-decoration:none;font-weight:600;border:1px solid #d0d7de;background:#2271b1;color:#fff;box-shadow:none;cursor:pointer}
                .gpfld-btn-secondary{background:#fff;color:#0f172a;border-color:#cbd5e1}
                .gpfld-btn-disabled{background:#e2e8f0;color:#64748b;border-color:#cbd5e1;cursor:not-allowed;opacity:.8}
                .gpfld-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:16px;margin-bottom:18px}
                .gpfld-card{background:#fff;border:1px solid #e2e8f0;border-radius:18px;padding:18px;box-shadow:0 10px 30px rgba(15,23,42,.04)}
                .gpfld-stat-label{font-size:13px;color:#64748b;margin-bottom:8px;font-weight:600}
                .gpfld-stat-value{font-size:28px;font-weight:800;color:#0f172a;line-height:1.1}
                .gpfld-stat-sub{font-size:12px;color:#64748b;margin-top:8px}
                .gpfld-layout{display:block}
                .gpfld-filters{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:12px;margin-bottom:16px}
                .gpfld-field label{display:block;font-size:12px;font-weight:700;color:#475569;margin-bottom:6px}
                .gpfld-field input,.gpfld-field select,.gpfld-field textarea{width:100%;border:1px solid #cbd5e1;border-radius:10px;padding:10px 12px;min-height:42px}
                .gpfld-field textarea{min-height:110px;resize:vertical}
                .gpfld-table-wrap{overflow:auto}
                .gpfld-table{width:100%;border-collapse:separate;border-spacing:0}
                .gpfld-table th,.gpfld-table td{padding:14px 12px;border-bottom:1px solid #e2e8f0;vertical-align:top}
                .gpfld-table th{font-size:12px;text-transform:uppercase;letter-spacing:.04em;color:#64748b;background:#f8fafc;text-align:left;position:sticky;top:0}
                .gpfld-badge{display:inline-flex;align-items:center;padding:6px 10px;border-radius:999px;font-size:12px;font-weight:700}
                .gpfld-badge-new{background:#dbeafe;color:#1d4ed8}
                .gpfld-badge-contacted{background:#fef3c7;color:#b45309}
                .gpfld-badge-qualified{background:#ede9fe;color:#6d28d9}
                .gpfld-badge-proposal_sent{background:#cffafe;color:#0f766e}
                .gpfld-badge-converted{background:#dcfce7;color:#15803d}
                .gpfld-badge-lost{background:#fee2e2;color:#b91c1c}
                .gpfld-source{display:inline-block;background:#f1f5f9;color:#334155;border-radius:999px;padding:5px 10px;font-size:12px;font-weight:700}
                .gpfld-name{font-weight:700;color:#0f172a}
                .gpfld-muted{font-size:12px;color:#64748b}
                .gpfld-side-card + .gpfld-side-card{margin-top:18px}
                .gpfld-detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
                .gpfld-kv{background:#f8fafc;border:1px solid #e2e8f0;border-radius:12px;padding:12px}
                .gpfld-kv strong{display:block;font-size:12px;color:#64748b;margin-bottom:6px;text-transform:uppercase}
                .gpfld-message-box,.gpfld-raw-box{background:#f8fafc;border:1px solid #e2e8f0;border-radius:14px;padding:14px;white-space:pre-wrap;word-break:break-word}
                .gpfld-raw-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
                .gpfld-raw-item{background:#f8fafc;border:1px solid #e2e8f0;border-radius:12px;padding:12px}
                .gpfld-raw-item strong{display:block;font-size:12px;color:#64748b;margin-bottom:6px;text-transform:uppercase}
                .gpfld-toolbar{display:flex;gap:10px;justify-content:space-between;align-items:center;flex-wrap:wrap;margin-bottom:14px}
                .gpfld-empty{padding:28px;text-align:center;color:#64748b}
                .gpfld-row-actions{display:flex;gap:8px;flex-wrap:wrap}
                .gpfld-card-spaced{margin-bottom:18px}
                .gpfld-help{margin:8px 0 0;color:#64748b;font-size:13px;line-height:1.5}
                .gpfld-section-intro{margin:-2px 0 14px;color:#475569;font-size:14px;line-height:1.6}
                .gpfld-form-card{margin-bottom:14px;padding:16px}
                .gpfld-form-card:last-child{margin-bottom:0}
                .gpfld-modal{position:fixed;inset:0;z-index:100000;display:flex;align-items:flex-start;justify-content:center;padding:40px 20px;background:rgba(15,23,42,.55);overflow:auto}
                .gpfld-modal[hidden]{display:none}
                .gpfld-modal-panel{width:min(1120px,100%);background:#fff;border:1px solid #e2e8f0;border-radius:18px;box-shadow:0 24px 70px rgba(15,23,42,.28)}
                .gpfld-modal-header{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:18px 20px;border-bottom:1px solid #e2e8f0}
                .gpfld-modal-header h2{margin:0;font-size:20px}
                .gpfld-modal-close{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:999px;border:1px solid #cbd5e1;background:#fff;color:#0f172a;text-decoration:none;font-size:22px;line-height:1}
                .gpfld-modal-body{padding:20px}
                .gpfld-modal-columns{display:grid;grid-template-columns:minmax(0,1.4fr) minmax(320px,.8fr);gap:18px;align-items:start}
                @media (max-width: 1200px){.gpfld-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.gpfld-filters{grid-template-columns:repeat(2,minmax(0,1fr))}.gpfld-modal-columns{grid-template-columns:1fr}}
                @media (max-width: 782px){.gpfld-grid,.gpfld-filters,.gpfld-detail-grid,.gpfld-raw-grid{grid-template-columns:1fr}.gpfld-header{align-items:flex-start}}
            ';
            wp_add_inline_style('gpfld-admin-style', $css);
        }

        private function maybe_json_encode($value) {
            if (is_array($value) || is_object($value)) {
                return wp_json_encode($value);
            }

            return trim((string) $value);
        }

        private function normalize_field_label($label) {
            $label = (string) $label;
            $label = str_replace(['_', '-'], ' ', $label);
            $label = preg_replace('/\s+/', ' ', $label);
            $label = trim($label);

            return ucwords($label);
        }

        private function format_raw_fields($fields) {
            $formatted = [];

            if (!is_array($fields)) {
                return $formatted;
            }

            foreach ($fields as $key => $value) {
                $label = is_string($key) && $key !== '' ? $this->normalize_field_label($key) : __('Field', 'gopdftools-form-leads-dashboard');
                $formatted[$label] = $this->maybe_json_encode($value);
            }

            return $formatted;
        }

        private function normalize_common_fields($fields) {
            $normalized = [
                'name' => '',
                'email' => '',
                'phone' => '',
                'subject' => '',
                'message' => '',
            ];

            if (!is_array($fields)) {
                return $normalized;
            }

            foreach ($fields as $key => $value) {
                $label = is_string($key) ? strtolower($key) : '';
                $string_value = is_array($value) ? wp_json_encode($value) : trim((string) $value);

                if ($normalized['email'] === '' && (strpos($label, 'email') !== false || filter_var($string_value, FILTER_VALIDATE_EMAIL))) {
                    $normalized['email'] = sanitize_email($string_value);
                    continue;
                }

                if ($normalized['phone'] === '' && (strpos($label, 'phone') !== false || strpos($label, 'mobile') !== false || strpos($label, 'whatsapp') !== false || preg_match('/^\+?[0-9\s\-\(\)]{7,}$/', $string_value))) {
                    $normalized['phone'] = sanitize_text_field($string_value);
                    continue;
                }

                if ($normalized['subject'] === '' && strpos($label, 'subject') !== false) {
                    $normalized['subject'] = sanitize_text_field($string_value);
                    continue;
                }

                if ($normalized['message'] === '' && (strpos($label, 'message') !== false || strpos($label, 'comment') !== false || strpos($label, 'details') !== false || strpos($label, 'project') !== false || strpos($label, 'description') !== false)) {
                    $normalized['message'] = wp_kses_post($string_value);
                    continue;
                }

                if ($normalized['name'] === '' && (strpos($label, 'name') !== false || strpos($label, 'full name') !== false)) {
                    $normalized['name'] = sanitize_text_field($string_value);
                    continue;
                }
            }

            if ($normalized['name'] === '' && !empty($fields)) {
                foreach ($fields as $value) {
                    if (is_scalar($value) && strlen((string) $value) < 120 && !filter_var((string) $value, FILTER_VALIDATE_EMAIL)) {
                        $normalized['name'] = sanitize_text_field((string) $value);
                        break;
                    }
                }
            }

            return $normalized;
        }

        private function get_request_ip() {
            $candidates = [
                'HTTP_CF_CONNECTING_IP',
                'HTTP_X_REAL_IP',
                'REMOTE_ADDR',
            ];

            foreach ($candidates as $key) {
                if (empty($_SERVER[$key])) {
                    continue;
                }

                $ip = trim((string) wp_unslash($_SERVER[$key]));
                if (filter_var($ip, FILTER_VALIDATE_IP)) {
                    return sanitize_text_field($ip);
                }
            }

            if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
                $forwarded = explode(',', (string) wp_unslash($_SERVER['HTTP_X_FORWARDED_FOR']));
                foreach ($forwarded as $ip) {
                    $ip = trim($ip);
                    if (filter_var($ip, FILTER_VALIDATE_IP)) {
                        return sanitize_text_field($ip);
                    }
                }
            }

            return '';
        }

        private function get_request_location() {
            $parts = [];
            $headers = [
                'HTTP_CF_IPCITY',
                'HTTP_CF_IPREGION',
                'HTTP_CF_IPCOUNTRY',
                'GEOIP_CITY',
                'GEOIP_REGION',
                'GEOIP_COUNTRY_NAME',
                'GEOIP_COUNTRY_CODE',
            ];

            foreach ($headers as $header) {
                if (empty($_SERVER[$header])) {
                    continue;
                }

                $value = sanitize_text_field(wp_unslash($_SERVER[$header]));
                if ($value !== '' && !in_array($value, $parts, true)) {
                    $parts[] = $value;
                }
            }

            return implode(', ', $parts);
        }

        private function insert_lead($data) {
            global $wpdb;

            $defaults = [
                'source_plugin' => '',
                'form_id' => '',
                'form_name' => '',
                'page_id' => null,
                'page_url' => '',
                'referrer_url' => '',
                'lead_name' => '',
                'lead_email' => '',
                'lead_phone' => '',
                'lead_subject' => '',
                'lead_message' => '',
                'ip_address' => $this->get_request_ip(),
                'location' => $this->get_request_location(),
                'raw_fields' => '',
                'status' => 'new',
                'notes' => '',
                'created_at' => current_time('mysql'),
                'updated_at' => current_time('mysql'),
            ];

            $payload = wp_parse_args($data, $defaults);
            $inserted = $wpdb->insert($this->table_name(), $payload);

            if ($inserted) {
                $lead = (object) array_merge($payload, ['id' => (int) $wpdb->insert_id]);
                return (int) $wpdb->insert_id;
            }

            return 0;
        }

        public function capture_wpforms($fields, $entry, $form_data, $entry_id) {
            if (empty($fields) || empty($form_data) || !is_array($fields)) {
                return;
            }

            $mapped_fields = [];
            foreach ($fields as $field) {
                $label = '';

                if (!empty($field['name'])) {
                    $label = $field['name'];
                } elseif (!empty($field['type']) && !empty($field['id'])) {
                    $label = $field['type'] . ' ' . $field['id'];
                } elseif (!empty($field['id'])) {
                    $label = 'field ' . $field['id'];
                }

                if ($label !== '') {
                    $mapped_fields[$label] = isset($field['value']) ? $field['value'] : '';
                }
            }

            $formatted_fields = $this->format_raw_fields($mapped_fields);
            $normalized = $this->normalize_common_fields($formatted_fields);
            $page_id = !empty($entry['post_id']) ? absint($entry['post_id']) : 0;
            $page_url = $page_id ? get_permalink($page_id) : (isset($_SERVER['HTTP_REFERER']) ? esc_url_raw(wp_unslash($_SERVER['HTTP_REFERER'])) : '');
            $referrer = isset($_SERVER['HTTP_REFERER']) ? esc_url_raw(wp_unslash($_SERVER['HTTP_REFERER'])) : '';

            $this->insert_lead([
                'source_plugin' => 'WPForms',
                'form_id' => isset($form_data['id']) ? (string) $form_data['id'] : '',
                'form_name' => !empty($form_data['settings']['form_title']) ? sanitize_text_field($form_data['settings']['form_title']) : 'WPForms Form',
                'page_id' => $page_id,
                'page_url' => $page_url,
                'referrer_url' => $referrer,
                'lead_name' => $normalized['name'],
                'lead_email' => $normalized['email'],
                'lead_phone' => $normalized['phone'],
                'lead_subject' => $normalized['subject'],
                'lead_message' => $normalized['message'],
                'raw_fields' => wp_json_encode($formatted_fields),
            ]);
        }

        public function capture_cf7($contact_form) {
    try {
        if (!class_exists('WPCF7_Submission')) {
            return;
        }

        $submission = WPCF7_Submission::get_instance();
        if (!$submission) {
            return;
        }

        $posted = $submission->get_posted_data();
        if (empty($posted) || !is_array($posted)) {
            return;
        }

        unset(
            $posted['_wpcf7'],
            $posted['_wpcf7_version'],
            $posted['_wpcf7_locale'],
            $posted['_wpcf7_unit_tag'],
            $posted['_wpcf7_container_post'],
            $posted['_wpcf7_posted_data_hash']
        );

        $formatted_fields = $this->format_raw_fields($posted);
        $normalized = $this->normalize_common_fields($formatted_fields);

        $referrer = '';
        if (isset($_SERVER['HTTP_REFERER'])) {
            $referrer = esc_url_raw(wp_unslash($_SERVER['HTTP_REFERER']));
        }

        $form_id = '';
        $form_name = 'Contact Form 7 Form';

        if (is_object($contact_form)) {
            if (method_exists($contact_form, 'id')) {
                $form_id = (string) $contact_form->id();
            }
            if (method_exists($contact_form, 'title')) {
                $form_name = sanitize_text_field($contact_form->title());
            }
        }

        $this->insert_lead([
            'source_plugin' => 'Contact Form 7',
            'form_id' => $form_id,
            'form_name' => $form_name,
            'page_url' => $referrer,
            'referrer_url' => $referrer,
            'lead_name' => $normalized['name'],
            'lead_email' => $normalized['email'],
            'lead_phone' => $normalized['phone'],
            'lead_subject' => $normalized['subject'],
            'lead_message' => $normalized['message'],
            'raw_fields' => wp_json_encode($formatted_fields),
        ]);
    } catch (Throwable $e) {
        if (defined('WP_DEBUG') && WP_DEBUG) {
            error_log('SFL CF7 error: ' . $e->getMessage());
        }
    }
}

        public function capture_elementor($record, $handler) {
            if (!is_object($record) || !method_exists($record, 'get')) {
                return;
            }

            $fields = $record->get('fields');
            if (empty($fields) || !is_array($fields)) {
                return;
            }

            $mapped_fields = [];
            foreach ($fields as $id => $field) {
                $label = !empty($field['title']) ? $field['title'] : $id;
                $mapped_fields[$label] = isset($field['value']) ? $field['value'] : '';
            }

            $formatted_fields = $this->format_raw_fields($mapped_fields);
            $normalized = $this->normalize_common_fields($formatted_fields);
            $form_name = method_exists($record, 'get_form_settings') ? $record->get_form_settings('form_name') : 'Elementor Form';
            $page_url = isset($_SERVER['HTTP_REFERER']) ? esc_url_raw(wp_unslash($_SERVER['HTTP_REFERER'])) : '';

            $this->insert_lead([
                'source_plugin' => 'Elementor Forms',
                'form_id' => sanitize_title($form_name),
                'form_name' => sanitize_text_field($form_name ?: 'Elementor Form'),
                'page_url' => $page_url,
                'referrer_url' => $page_url,
                'lead_name' => $normalized['name'],
                'lead_email' => $normalized['email'],
                'lead_phone' => $normalized['phone'],
                'lead_subject' => $normalized['subject'],
                'lead_message' => $normalized['message'],
                'raw_fields' => wp_json_encode($formatted_fields),
            ]);
        }

        private function get_stats() {
            global $wpdb;
            $table = $this->table_name();

            $total = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$table}");
            $new = (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$table} WHERE status = %s", 'new'));
            $converted = (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$table} WHERE status = %s", 'converted'));
            $this_month = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE DATE_FORMAT(created_at, '%Y-%m') = DATE_FORMAT(NOW(), '%Y-%m')");

            return [
                'total' => $total,
                'new' => $new,
                'converted' => $converted,
                'this_month' => $this_month,
            ];
        }

        private function get_leads($args = []) {
            global $wpdb;
            $table = $this->table_name();

            $defaults = [
                'search' => '',
                'status' => '',
                'source_plugin' => '',
                'form_name' => '',
                'per_page' => 20,
                'paged' => 1,
            ];

            $args = wp_parse_args($args, $defaults);
            $where = ['1=1'];
            $params = [];

            if ($args['search'] !== '') {
                $like = '%' . $wpdb->esc_like($args['search']) . '%';
                $where[] = '(lead_name LIKE %s OR lead_email LIKE %s OR lead_phone LIKE %s OR form_name LIKE %s OR page_url LIKE %s OR ip_address LIKE %s OR location LIKE %s OR raw_fields LIKE %s)';
                array_push($params, $like, $like, $like, $like, $like, $like, $like, $like);
            }

            if ($args['status'] !== '') {
                $where[] = 'status = %s';
                $params[] = $args['status'];
            }

            if ($args['source_plugin'] !== '') {
                $where[] = 'source_plugin = %s';
                $params[] = $args['source_plugin'];
            }

            if ($args['form_name'] !== '') {
                $where[] = 'form_name = %s';
                $params[] = $args['form_name'];
            }

            $where_sql = implode(' AND ', $where);
            $offset = max(0, ($args['paged'] - 1) * $args['per_page']);

            $count_sql = "SELECT COUNT(*) FROM {$table} WHERE {$where_sql}";
            $data_sql = "SELECT * FROM {$table} WHERE {$where_sql} ORDER BY created_at DESC LIMIT %d OFFSET %d";

            $prepared_count_sql = !empty($params) ? $wpdb->prepare($count_sql, $params) : $count_sql;
            $prepared_data_sql = $wpdb->prepare($data_sql, array_merge($params, [$args['per_page'], $offset]));

            $total = (int) $wpdb->get_var($prepared_count_sql);
            $rows = $wpdb->get_results($prepared_data_sql);

            return [
                'rows' => $rows,
                'total' => $total,
            ];
        }

        private function get_lead($id) {
            global $wpdb;
            return $wpdb->get_row($wpdb->prepare("SELECT * FROM {$this->table_name()} WHERE id = %d", $id));
        }

        private function get_distinct_values($column) {
            global $wpdb;
            $allowed = ['source_plugin', 'form_name'];

            if (!in_array($column, $allowed, true)) {
                return [];
            }

            return $wpdb->get_col("SELECT DISTINCT {$column} FROM {$this->table_name()} WHERE {$column} <> '' ORDER BY {$column} ASC");
        }

        private function build_url($args = []) {
            return add_query_arg(array_merge(['page' => self::MENU_SLUG], $args), admin_url('admin.php'));
        }

        private function render_stat_card($label, $value, $sub) {
            return '<div class="gpfld-card"><div class="gpfld-stat-label">' . esc_html($label) . '</div><div class="gpfld-stat-value">' . esc_html($value) . '</div><div class="gpfld-stat-sub">' . esc_html($sub) . '</div></div>';
        }

        private function render_status_badge($status) {
            $statuses = $this->statuses();
            $label = isset($statuses[$status]) ? $statuses[$status] : ucfirst((string) $status);
            return '<span class="gpfld-badge gpfld-badge-' . esc_attr($status) . '">' . esc_html($label) . '</span>';
        }

        public function render_dashboard_page() {
            if (!current_user_can(self::CAP)) {
                wp_die(esc_html__('Unauthorized', 'gopdftools-form-leads-dashboard'));
            }

            $search = $this->get_param('s', '');
            $status = $this->get_param('status', '');
            $source_plugin = $this->get_param('source_plugin', '');
            $form_name = $this->get_param('form_name', '');
            $paged = max(1, absint($this->get_param('paged', 1)));
            $view_lead_id = absint($this->get_param('view_lead', 0));

            $stats = $this->get_stats();
            $result = $this->get_leads([
                'search' => $search,
                'status' => $status,
                'source_plugin' => $source_plugin,
                'form_name' => $form_name,
                'paged' => $paged,
                'per_page' => 20,
            ]);

            $leads = $result['rows'];
            $total = $result['total'];
            $total_pages = max(1, (int) ceil($total / 20));
            $selected_lead = $view_lead_id ? $this->get_lead($view_lead_id) : null;
            $statuses = $this->statuses();
            $source_plugins = $this->get_distinct_values('source_plugin');
            $form_names = $this->get_distinct_values('form_name');

            echo '<div class="wrap gpfld-wrap">';
            echo '<div class="gpfld-header">';
            echo '<div class="gpfld-title"><h1>' . esc_html__('GoPDFTools Form Leads Dashboard', 'gopdftools-form-leads-dashboard') . '</h1><p>' . esc_html__('CRM-style lead management for WPForms, Contact Form 7, and Elementor Forms.', 'gopdftools-form-leads-dashboard') . '</p></div>';
            echo '<div class="gpfld-actions">';
            echo '<a class="gpfld-btn-secondary" href="' . esc_url($this->build_url()) . '">' . esc_html__('Refresh', 'gopdftools-form-leads-dashboard') . '</a>';
            echo '<a class="gpfld-btn" href="' . esc_url(wp_nonce_url(admin_url('admin-post.php?action=gpfld_export_csv'), self::NONCE_ACTION)) . '">' . esc_html__('Export CSV', 'gopdftools-form-leads-dashboard') . '</a>';
            echo '</div></div>';

            echo '<div class="gpfld-grid">';
            echo wp_kses_post($this->render_stat_card(__('Total Leads', 'gopdftools-form-leads-dashboard'), (string) $stats['total'], __('All captured leads across supported form plugins.', 'gopdftools-form-leads-dashboard')));
            echo wp_kses_post($this->render_stat_card(__('New Leads', 'gopdftools-form-leads-dashboard'), (string) $stats['new'], __('Leads currently waiting for follow-up.', 'gopdftools-form-leads-dashboard')));
            echo wp_kses_post($this->render_stat_card(__('Converted', 'gopdftools-form-leads-dashboard'), (string) $stats['converted'], __('Marked as successfully converted.', 'gopdftools-form-leads-dashboard')));
            echo wp_kses_post($this->render_stat_card(__('This Month', 'gopdftools-form-leads-dashboard'), (string) $stats['this_month'], __('Leads received in the current month.', 'gopdftools-form-leads-dashboard')));
            echo '</div>';

            echo '<div class="gpfld-layout">';

            echo '<div class="gpfld-card">';
            echo '<div class="gpfld-toolbar"><h2 style="margin:0;font-size:20px;">' . esc_html__('Leads CRM', 'gopdftools-form-leads-dashboard') . '</h2><span class="gpfld-muted">' . esc_html(sprintf(__('Showing %1$d of %2$d leads', 'gopdftools-form-leads-dashboard'), count($leads), $total)) . '</span></div>';

            echo '<form method="get">';
            echo '<input type="hidden" name="page" value="' . esc_attr(self::MENU_SLUG) . '">';
            echo '<div class="gpfld-filters">';
            echo '<div class="gpfld-field"><label>' . esc_html__('Search', 'gopdftools-form-leads-dashboard') . '</label><input type="text" name="s" value="' . esc_attr($search) . '" placeholder="' . esc_attr__('Name, email, phone, form, field value...', 'gopdftools-form-leads-dashboard') . '"></div>';
            echo '<div class="gpfld-field"><label>' . esc_html__('Status', 'gopdftools-form-leads-dashboard') . '</label><select name="status"><option value="">' . esc_html__('All statuses', 'gopdftools-form-leads-dashboard') . '</option>';
            foreach ($statuses as $key => $label) {
                echo '<option value="' . esc_attr($key) . '" ' . selected($status, $key, false) . '>' . esc_html($label) . '</option>';
            }
            echo '</select></div>';
            echo '<div class="gpfld-field"><label>' . esc_html__('Source Plugin', 'gopdftools-form-leads-dashboard') . '</label><select name="source_plugin"><option value="">' . esc_html__('All sources', 'gopdftools-form-leads-dashboard') . '</option>';
            foreach ($source_plugins as $plugin) {
                echo '<option value="' . esc_attr($plugin) . '" ' . selected($source_plugin, $plugin, false) . '>' . esc_html($plugin) . '</option>';
            }
            echo '</select></div>';
            echo '<div class="gpfld-field"><label>' . esc_html__('Form Name', 'gopdftools-form-leads-dashboard') . '</label><select name="form_name"><option value="">' . esc_html__('All forms', 'gopdftools-form-leads-dashboard') . '</option>';
            foreach ($form_names as $form) {
                echo '<option value="' . esc_attr($form) . '" ' . selected($form_name, $form, false) . '>' . esc_html($form) . '</option>';
            }
            echo '</select></div>';
            echo '<div class="gpfld-field"><label>&nbsp;</label><button class="gpfld-btn" type="submit" style="width:100%;">' . esc_html__('Apply Filters', 'gopdftools-form-leads-dashboard') . '</button></div>';
            echo '</div>';
            echo '</form>';

            echo '<div class="gpfld-table-wrap">';
            echo '<table class="gpfld-table">';
            echo '<thead><tr><th>' . esc_html__('Lead', 'gopdftools-form-leads-dashboard') . '</th><th>' . esc_html__('Source', 'gopdftools-form-leads-dashboard') . '</th><th>' . esc_html__('Form', 'gopdftools-form-leads-dashboard') . '</th><th>' . esc_html__('Page', 'gopdftools-form-leads-dashboard') . '</th><th>' . esc_html__('IP / Location', 'gopdftools-form-leads-dashboard') . '</th><th>' . esc_html__('Status', 'gopdftools-form-leads-dashboard') . '</th><th>' . esc_html__('Date', 'gopdftools-form-leads-dashboard') . '</th><th>' . esc_html__('Actions', 'gopdftools-form-leads-dashboard') . '</th></tr></thead><tbody>';

            if (!empty($leads)) {
                foreach ($leads as $lead) {
                    $view_url = add_query_arg([
                        'page' => self::MENU_SLUG,
                        'view_lead' => $lead->id,
                        's' => $search,
                        'status' => $status,
                        'source_plugin' => $source_plugin,
                        'form_name' => $form_name,
                        'paged' => $paged,
                    ], admin_url('admin.php'));

                    echo '<tr>';
                    echo '<td><div class="gpfld-name">' . esc_html($lead->lead_name ?: 'â€”') . '</div>';
                    echo '<div class="gpfld-muted">' . esc_html($lead->lead_email ?: __('No email', 'gopdftools-form-leads-dashboard')) . '</div>';
                    echo '<div class="gpfld-muted">' . esc_html($lead->lead_phone ?: __('No phone', 'gopdftools-form-leads-dashboard')) . '</div></td>';
                    echo '<td><span class="gpfld-source">' . esc_html($lead->source_plugin ?: __('Unknown', 'gopdftools-form-leads-dashboard')) . '</span></td>';
                    echo '<td><strong>' . esc_html($lead->form_name ?: 'â€”') . '</strong><div class="gpfld-muted">ID: ' . esc_html($lead->form_id ?: 'â€”') . '</div></td>';
                    echo '<td>';
                    if (!empty($lead->page_url)) {
                        echo '<a href="' . esc_url($lead->page_url) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('Open page', 'gopdftools-form-leads-dashboard') . '</a>';
                    } else {
                        echo 'â€”';
                    }
                    echo '</td>';
                    echo '<td><div class="gpfld-name">' . esc_html(!empty($lead->ip_address) ? $lead->ip_address : '-') . '</div><div class="gpfld-muted">' . esc_html(!empty($lead->location) ? $lead->location : __('Location unavailable', 'gopdftools-form-leads-dashboard')) . '</div></td>';
                    echo '<td>' . wp_kses_post($this->render_status_badge($lead->status)) . '</td>';
                    echo '<td><strong>' . esc_html(mysql2date('M j, Y', $lead->created_at)) . '</strong><div class="gpfld-muted">' . esc_html(mysql2date('g:i a', $lead->created_at)) . '</div></td>';
                    echo '<td><div class="gpfld-row-actions">';
                    echo '<a class="gpfld-btn-secondary" href="' . esc_url($view_url) . '">' . esc_html__('View Detail', 'gopdftools-form-leads-dashboard') . '</a>';
                    echo '</div></td>';
                    echo '</tr>';
                }
            } else {
                echo '<tr><td colspan="8"><div class="gpfld-empty">' . esc_html__('No leads found yet. Submit a supported form to start building your CRM dashboard.', 'gopdftools-form-leads-dashboard') . '</div></td></tr>';
            }

            echo '</tbody></table></div>';

            if ($total_pages > 1) {
                echo '<div style="margin-top:16px;">' . wp_kses_post(paginate_links([
                    'base' => add_query_arg('paged', '%#%'),
                    'format' => '',
                    'current' => $paged,
                    'total' => $total_pages,
                    'type' => 'plain',
                ])) . '</div>';
            }

            echo '</div>';

            if ($selected_lead) {
                $raw_fields = !empty($selected_lead->raw_fields) ? json_decode($selected_lead->raw_fields, true) : [];
                if (!is_array($raw_fields)) {
                    $raw_fields = [];
                }

                $close_url = remove_query_arg('view_lead');

                echo '<div class="gpfld-modal" role="dialog" aria-modal="true" aria-labelledby="gpfld-lead-detail-title">';
                echo '<div class="gpfld-modal-panel">';
                echo '<div class="gpfld-modal-header"><div class="gpfld-toolbar" style="margin:0;"><h2 id="gpfld-lead-detail-title">' . esc_html__('Lead Detail', 'gopdftools-form-leads-dashboard') . '</h2>' . wp_kses_post($this->render_status_badge($selected_lead->status)) . '</div><a class="gpfld-modal-close" href="' . esc_url($close_url) . '" aria-label="' . esc_attr__('Close lead detail', 'gopdftools-form-leads-dashboard') . '">&times;</a></div>';
                echo '<div class="gpfld-modal-body">';
                echo '<div class="gpfld-modal-columns">';
                echo '<div>';
                echo '<div class="gpfld-card gpfld-side-card">';
                echo '<div class="gpfld-detail-grid">';
                echo '<div class="gpfld-kv"><strong>' . esc_html__('Name', 'gopdftools-form-leads-dashboard') . '</strong>' . esc_html($selected_lead->lead_name ?: 'â€”') . '</div>';
                echo '<div class="gpfld-kv"><strong>' . esc_html__('Email', 'gopdftools-form-leads-dashboard') . '</strong>' . esc_html($selected_lead->lead_email ?: 'â€”') . '</div>';
                echo '<div class="gpfld-kv"><strong>' . esc_html__('Phone', 'gopdftools-form-leads-dashboard') . '</strong>' . esc_html($selected_lead->lead_phone ?: 'â€”') . '</div>';
                echo '<div class="gpfld-kv"><strong>' . esc_html__('Source', 'gopdftools-form-leads-dashboard') . '</strong>' . esc_html($selected_lead->source_plugin ?: 'â€”') . '</div>';
                echo '<div class="gpfld-kv"><strong>' . esc_html__('Form Name', 'gopdftools-form-leads-dashboard') . '</strong>' . esc_html($selected_lead->form_name ?: 'â€”') . '</div>';
                echo '<div class="gpfld-kv"><strong>' . esc_html__('IP Address', 'gopdftools-form-leads-dashboard') . '</strong>' . esc_html(!empty($selected_lead->ip_address) ? $selected_lead->ip_address : '-') . '</div>';
                echo '<div class="gpfld-kv"><strong>' . esc_html__('Location', 'gopdftools-form-leads-dashboard') . '</strong>' . esc_html(!empty($selected_lead->location) ? $selected_lead->location : __('Location unavailable', 'gopdftools-form-leads-dashboard')) . '</div>';
                echo '<div class="gpfld-kv"><strong>' . esc_html__('Date', 'gopdftools-form-leads-dashboard') . '</strong>' . esc_html(mysql2date('M j, Y g:i a', $selected_lead->created_at)) . '</div>';
                echo '</div>';

                echo '<div style="margin-top:14px;">';
                echo '<strong style="display:block;margin-bottom:8px;">' . esc_html__('Message', 'gopdftools-form-leads-dashboard') . '</strong>';
                echo '<div class="gpfld-message-box">' . wp_kses_post($selected_lead->lead_message ?: __('No message captured.', 'gopdftools-form-leads-dashboard')) . '</div>';
                echo '</div>';

                echo '<div style="margin-top:14px;">';
                echo '<strong style="display:block;margin-bottom:8px;">' . esc_html__('Captured Page', 'gopdftools-form-leads-dashboard') . '</strong>';
                if (!empty($selected_lead->page_url)) {
                    echo '<div class="gpfld-message-box"><a href="' . esc_url($selected_lead->page_url) . '" target="_blank" rel="noopener noreferrer">' . esc_html($selected_lead->page_url) . '</a></div>';
                } else {
                    echo '<div class="gpfld-message-box">' . esc_html__('No page URL available.', 'gopdftools-form-leads-dashboard') . '</div>';
                }
                echo '</div>';
                echo '</div>';

                echo '</div><div>';
                echo '<div class="gpfld-card gpfld-side-card">';
                echo '<h2 style="margin-top:0;font-size:20px;">' . esc_html__('CRM Actions', 'gopdftools-form-leads-dashboard') . '</h2>';
                echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '">';
                wp_nonce_field(self::NONCE_ACTION);
                echo '<input type="hidden" name="action" value="gpfld_update_lead">';
                echo '<input type="hidden" name="lead_id" value="' . esc_attr((string) $selected_lead->id) . '">';
                echo '<input type="hidden" name="redirect_url" value="' . esc_attr(add_query_arg(['page' => self::MENU_SLUG, 'view_lead' => $selected_lead->id], admin_url('admin.php'))) . '">';

                echo '<div class="gpfld-field"><label>' . esc_html__('Status', 'gopdftools-form-leads-dashboard') . '</label><select name="status">';
                foreach ($statuses as $key => $label) {
                    echo '<option value="' . esc_attr($key) . '" ' . selected($selected_lead->status, $key, false) . '>' . esc_html($label) . '</option>';
                }
                echo '</select></div>';

                echo '<div class="gpfld-field" style="margin-top:12px;"><label>' . esc_html__('Internal Notes', 'gopdftools-form-leads-dashboard') . '</label><textarea name="notes" placeholder="' . esc_attr__('Add call notes, budget info, follow-up details...', 'gopdftools-form-leads-dashboard') . '">' . esc_textarea($selected_lead->notes) . '</textarea></div>';

                echo '<div class="gpfld-actions" style="margin-top:14px;">';
                echo '<button type="submit" class="gpfld-btn">' . esc_html__('Save Lead', 'gopdftools-form-leads-dashboard') . '</button>';
                echo '</div>';
                echo '</form>';

                echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" onsubmit="' . esc_attr(sprintf("return confirm('%s');", esc_js(__('Delete this lead?', 'gopdftools-form-leads-dashboard')))) . '" style="margin-top:14px;">';
                wp_nonce_field(self::NONCE_ACTION);
                echo '<input type="hidden" name="action" value="gpfld_delete_lead">';
                echo '<input type="hidden" name="lead_id" value="' . esc_attr((string) $selected_lead->id) . '">';
                echo '<input type="hidden" name="redirect_url" value="' . esc_attr(add_query_arg(['page' => self::MENU_SLUG], admin_url('admin.php'))) . '">';
                echo '<button type="submit" class="gpfld-btn-secondary">' . esc_html__('Delete Lead', 'gopdftools-form-leads-dashboard') . '</button>';
                echo '</form>';
                echo '</div>';

                echo '<div class="gpfld-card gpfld-side-card">';
                echo '<h2 style="margin-top:0;font-size:20px;">' . esc_html__('All Captured Fields', 'gopdftools-form-leads-dashboard') . '</h2>';
                if (!empty($raw_fields)) {
                    echo '<div class="gpfld-raw-grid">';
                    foreach ($raw_fields as $field_key => $field_value) {
                        echo '<div class="gpfld-raw-item"><strong>' . esc_html((string) $field_key) . '</strong>' . wp_kses_post(nl2br(esc_html(is_array($field_value) ? wp_json_encode($field_value) : (string) $field_value))) . '</div>';
                    }
                    echo '</div>';
                } else {
                    echo '<div class="gpfld-message-box">' . esc_html__('No raw fields saved.', 'gopdftools-form-leads-dashboard') . '</div>';
                }
                echo '</div>';
                echo '</div>';
                echo '</div>';
                echo '</div>';
                echo '</div>';
                echo '</div>';
            }

            echo '</div>';
            echo '</div>';
        }

       public function handle_update_lead() {
    if (!current_user_can(self::CAP)) {
        wp_die(esc_html__('Unauthorized', 'gopdftools-form-leads-dashboard'));
    }

    check_admin_referer(self::NONCE_ACTION);

    global $wpdb;

    $lead_id = isset($_POST['lead_id']) ? absint($_POST['lead_id']) : 0;
    $status  = isset($_POST['status']) ? sanitize_text_field(wp_unslash($_POST['status'])) : 'new';
    $notes   = isset($_POST['notes']) ? sanitize_textarea_field(wp_unslash($_POST['notes'])) : '';

    if (isset($_POST['redirect_url'])) {
        $redirect_url = esc_url_raw(wp_unslash($_POST['redirect_url']));
    } else {
        $redirect_url = esc_url_raw($this->build_url());
    }

    if ($lead_id) {
        $wpdb->update(
            $this->table_name(),
            [
                'status'     => $status,
                'notes'      => $notes,
                'updated_at' => current_time('mysql'),
            ],
            ['id' => $lead_id],
            ['%s', '%s', '%s'],
            ['%d']
        );
    }

    wp_safe_redirect($redirect_url);
    exit;
}

        public function handle_delete_lead() {
    if (!current_user_can(self::CAP)) {
        wp_die(esc_html__('Unauthorized', 'gopdftools-form-leads-dashboard'));
    }

    check_admin_referer(self::NONCE_ACTION);

    global $wpdb;

    $lead_id = isset($_POST['lead_id']) ? absint($_POST['lead_id']) : 0;

    if (isset($_POST['redirect_url'])) {
        $redirect_url = esc_url_raw(wp_unslash($_POST['redirect_url']));
    } else {
        $redirect_url = esc_url_raw($this->build_url());
    }

    if ($lead_id) {
        $wpdb->delete($this->table_name(), ['id' => $lead_id], ['%d']);
    }

    wp_safe_redirect($redirect_url);
    exit;
}

        public function handle_export_csv() {
            if (!current_user_can(self::CAP)) {
                wp_die(esc_html__('Unauthorized', 'gopdftools-form-leads-dashboard'));
            }

            check_admin_referer(self::NONCE_ACTION);

            global $wpdb;
            $rows = $wpdb->get_results("SELECT * FROM {$this->table_name()} ORDER BY created_at DESC", ARRAY_A);

            nocache_headers();
            header('Content-Type: text/csv; charset=utf-8');
            header('Content-Disposition: attachment; filename=gopdftools-form-leads-' . gmdate('Y-m-d-His') . '.csv');

            $output = fopen('php://output', 'w');
            fputcsv($output, ['ID', 'Source Plugin', 'Form ID', 'Form Name', 'Name', 'Email', 'Phone', 'Subject', 'Message', 'IP Address', 'Location', 'Page URL', 'Referrer URL', 'Status', 'Notes', 'Captured Fields', 'Created At']);

            foreach ($rows as $row) {
                fputcsv($output, [
                    $row['id'],
                    $row['source_plugin'],
                    $row['form_id'],
                    $row['form_name'],
                    $row['lead_name'],
                    $row['lead_email'],
                    $row['lead_phone'],
                    $row['lead_subject'],
                    $row['lead_message'],
                    isset($row['ip_address']) ? $row['ip_address'] : '',
                    isset($row['location']) ? $row['location'] : '',
                    $row['page_url'],
                    $row['referrer_url'],
                    $row['status'],
                    $row['notes'],
                    $row['raw_fields'],
                    $row['created_at'],
                ]);
            }

            fclose($output);
            exit;
        }
    }

    new GPFLD_Form_Leads_Dashboard();
}
