<?php

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

class AIEO_Competition_Participants {

    const SCHEMA_OPTION  = 'aieo_competitions_schema_version';
    const SCHEMA_VERSION = '2'; 

    
    
    
    
    const LIST_OPTION = 'aieo_competitions_list_id';
    const LIST_COHORT = 'competitions';

    private static $registered = false;

    public static function register() {
        if (self::$registered) { return; }
        self::$registered = true;
        add_action('plugins_loaded', array(__CLASS__, 'maybe_install'), 25);
        add_action('gform_after_submission', array(__CLASS__, 'on_gform_submission'), 20, 2);
        add_action('newsletter_user_confirmed', array(__CLASS__, 'on_tnp_confirmed'), 10, 1);
    }

    public static function table() {
        global $wpdb;
        return $wpdb->prefix . 'aieo_competition_participants';
    }

    
    public static function maybe_install() {
        if (get_option(self::SCHEMA_OPTION) === self::SCHEMA_VERSION) { return; }
        if (!defined('AIEO_DIR')) { return; }
        $schema = AIEO_DIR . 'includes/dmm/competitions/sql/competitions-schema.php';
        if (!file_exists($schema)) { return; }
        require_once $schema;
        if (function_exists('aieo_competitions_install_schema')) {
            aieo_competitions_install_schema();
            update_option(self::SCHEMA_OPTION, self::SCHEMA_VERSION, false);
        }
    }

    

    public static function on_gform_submission($entry, $form) {
        if (!is_array($entry) || !is_array($form)) { return; }
        $form_id = (int) ($form['id'] ?? 0);
        if ($form_id <= 0) { return; }
        $competition_id = self::competition_for_form($form_id);
        if ($competition_id <= 0) { return; } 
        
        
        if (class_exists('AIEO_Competition_CPT') && AIEO_Competition_CPT::schedule_state($competition_id) !== 'live') { return; }
        self::maybe_install(); 
        self::capture_participant($competition_id, $entry, $form);
    }

    
    private static function competition_for_form($form_id) {
        if (!class_exists('AIEO_Competition_CPT')) { return 0; }
        $ids = get_posts(array(
            'post_type'     => AIEO_Competition_CPT::POST_TYPE,
            'post_status'   => 'publish',
            'numberposts'   => 1,
            'fields'        => 'ids',
            'no_found_rows' => true,
            'meta_key'      => AIEO_Competition_CPT::META_GF,
            'meta_value'    => (int) $form_id,
        ));
        return $ids ? (int) $ids[0] : 0;
    }

    private static function capture_participant($competition_id, $entry, $form) {
        list($email, $first, $last) = self::extract_email_name($entry, $form);
        if ($email === '' || !is_email($email)) { return; }

        $resolved = self::resolve_eponymous_for_email($email);
        $verified = self::is_verified_email($email, (int) $resolved['eponymous_id']);

        
        
        self::subscribe_to_competition_list(
            (int) $competition_id, $email, $first, $last,
            $verified ? 'confirmed' : 'not_confirmed'
        );

        $row = array(
            'competition_id' => (int) $competition_id,
            'gf_entry_id'    => (int) ($entry['id'] ?? 0),
            'email'          => $email,
            'first_name'     => $first,
            'last_name'      => $last,
            'status'         => $verified ? 'confirmed' : 'pending',
            'source'         => $verified ? 'customer' : 'optin',
            'eponymous_id'   => (int) $resolved['eponymous_id'],
            'wp_user_id'     => (int) $resolved['wp_user_id'],
            'created_at'     => current_time('mysql', true),
        );
        if ($verified) {
            $row['confirmed_at'] = current_time('mysql', true);
        }
        self::upsert_participant($row);

        
        
        
        
        if (class_exists('AIEO_Competition_Analytics')
            && class_exists('AIEO_DMM_Competitions')
            && 'off' !== AIEO_DMM_Competitions::get_stats_cadence()) {
            $once = AIEO_Competition_Analytics::STATS_REFRESH_HOOK . '_once';
            if (!wp_next_scheduled($once)) {
                wp_schedule_single_event(time() + 180, $once);
            }
        }
    }

    
    private static function extract_email_name($entry, $form) {
        $email = ''; $first = ''; $last = '';
        $fields = (is_array($form) && isset($form['fields']) && is_array($form['fields'])) ? $form['fields'] : array();
        foreach ($fields as $f) {
            $type = is_object($f) ? (string) ($f->type ?? '') : (string) ($f['type'] ?? '');
            $fid  = is_object($f) ? (string) ($f->id ?? '')   : (string) ($f['id'] ?? '');
            if ($fid === '') { continue; }
            if ($type === 'email' && $email === '') {
                $email = strtolower(trim((string) self::rgar($entry, $fid)));
            } elseif ($type === 'name') {
                $f3 = trim((string) self::rgar($entry, $fid . '.3')); 
                $f6 = trim((string) self::rgar($entry, $fid . '.6')); 
                if ($first === '' && $f3 !== '') { $first = $f3; }
                if ($last === ''  && $f6 !== '') { $last  = $f6; }
                if ($first === '') {
                    $whole = trim((string) self::rgar($entry, $fid)); 
                    if ($whole !== '') { $first = $whole; }
                }
            }
        }
        
        if ($email === '') {
            foreach ((array) $entry as $v) {
                if (is_string($v) && is_email(trim($v))) { $email = strtolower(trim($v)); break; }
            }
        }
        return array($email, $first, $last);
    }

    private static function rgar($arr, $key) {
        return (is_array($arr) && isset($arr[$key])) ? $arr[$key] : '';
    }

    

    
    private static function is_verified_email($email, $eponymous_id) {
        return (email_exists($email) !== false) || ((int) $eponymous_id > 0);
    }

    
    private static function resolve_eponymous_for_email($email) {
        global $wpdb;
        $out = array('eponymous_id' => 0, 'wp_user_id' => 0);
        $email = strtolower(trim((string) $email));
        if ($email === '') { return $out; }

        static $has_ci = null;
        $ci = $wpdb->prefix . 'aieo_customer_intelligence';
        if ($has_ci === null) {
            $has_ci = ((string) $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $ci)) === $ci);
        }
        if (!$has_ci) { return $out; }

        $row = $wpdb->get_row($wpdb->prepare(
            "SELECT eponymous_id, wp_user_id FROM {$ci}
              WHERE LOWER(TRIM(email)) COLLATE utf8mb4_unicode_ci = %s COLLATE utf8mb4_unicode_ci
              LIMIT 1",
            $email
        ));
        if ($row) {
            $out['eponymous_id'] = (int) $row->eponymous_id;
            $out['wp_user_id']   = (int) $row->wp_user_id;
        }
        return $out;
    }

    

    
    private static function subscribe_to_competition_list($competition_id, $email, $first, $last, $tnp_status) {
        if (!class_exists('AIEO_Newsletter_Registry') && defined('AIEO_DIR')) {
            $reg = AIEO_DIR . 'includes/dmm/push/class-aieo-newsletter-registry.php';
            if (file_exists($reg)) { require_once $reg; }
        }
        if (!class_exists('AIEO_Newsletter_Registry')) { return array(); }
        $adapter = AIEO_Newsletter_Registry::instance()->adapter();
        if (!$adapter || !method_exists($adapter, 'subscribe_one')) { return array(); }

        
        
        
        
        $opts = array(
            'status'     => $tnp_status,
            'first_name' => $first,
            'last_name'  => $last,
            'cohort'     => self::LIST_COHORT,
        );

        $res = $adapter->subscribe_one($email, $opts);
        
        
        if (is_array($res) && !empty($res['list_id']) && (int) get_option(self::LIST_OPTION, 0) <= 0) {
            update_option(self::LIST_OPTION, (int) $res['list_id'], false);
        }
        return is_array($res) ? $res : array();
    }

    
    public static function on_tnp_confirmed($user) {
        global $wpdb;
        if (!is_object($user) || empty($user->email)) { return; }
        $email = strtolower(trim((string) $user->email));
        if ($email === '') { return; }

        
        
        
        
        $list_id = (int) get_option(self::LIST_OPTION, 0);
        if ($list_id > 0) {
            $col = 'list_' . $list_id;
            if (empty($user->$col)) { return; }
        }

        $tbl = self::table();
        $wpdb->query($wpdb->prepare(
            "UPDATE {$tbl} SET status = 'confirmed', confirmed_at = %s
              WHERE LOWER(TRIM(email)) = %s AND status = 'pending'",
            current_time('mysql', true), $email
        ));
    }

    

    private static function upsert_participant(array $row) {
        global $wpdb;
        $tbl  = self::table();
        $cols = array_keys($row);
        $place = implode(', ', array_fill(0, count($cols), '%s'));
        $assigns = array();
        foreach ($cols as $c) {
            if ($c === 'competition_id' || $c === 'gf_entry_id') { continue; } 
            $assigns[] = $c . ' = VALUES(' . $c . ')';
        }
        $sql = "INSERT INTO {$tbl} (" . implode(', ', $cols) . ") VALUES ({$place})
                ON DUPLICATE KEY UPDATE " . implode(', ', $assigns) . ", updated_at = UTC_TIMESTAMP()";
        $wpdb->query($wpdb->prepare($sql, ...array_values($row)));
    }

    

    
    public static function metrics($competition_id) {
        global $wpdb;
        $tbl = self::table();
        $out = array('entries' => 0, 'confirmed' => 0, 'pending' => 0, 'winners' => 0);
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT status, COUNT(*) AS n FROM {$tbl} WHERE competition_id = %d GROUP BY status",
            (int) $competition_id
        ));
        if ($rows) {
            foreach ($rows as $r) {
                $out['entries'] += (int) $r->n;
                if ($r->status === 'confirmed') { $out['confirmed'] += (int) $r->n; }
                elseif ($r->status === 'pending') { $out['pending'] += (int) $r->n; }
            }
        }
        $out['winners'] = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$tbl} WHERE competition_id = %d AND is_winner = 1",
            (int) $competition_id
        ));
        return $out;
    }

    

    
    public static function draw_winners($competition_id, $count) {
        global $wpdb;
        $tbl   = self::table();
        $cid   = (int) $competition_id;
        $count = max(1, (int) $count);
        self::clear_winners($cid);
        $ids = $wpdb->get_col($wpdb->prepare(
            "SELECT pid FROM (
                SELECT MIN(cp.participant_id) AS pid,
                       COUNT(DISTINCT cp2.competition_id) AS weight
                  FROM {$tbl} cp
                  JOIN {$tbl} cp2 ON cp2.email = cp.email AND cp2.status = 'confirmed'
                 WHERE cp.competition_id = %d AND cp.status = 'confirmed'
                   AND NOT EXISTS (
                       SELECT 1 FROM {$tbl} w WHERE w.email = cp.email AND w.is_winner = 1
                   )
                 GROUP BY cp.email
             ) cand
             ORDER BY POW(RAND(), 1.0 / GREATEST(weight, 1)) DESC
             LIMIT %d",
            $cid, $count
        ));
        if ($ids) {
            $in = implode(',', array_map('intval', $ids));
            $wpdb->query($wpdb->prepare(
                "UPDATE {$tbl} SET is_winner = 1, won_at = %s WHERE participant_id IN ({$in})",
                current_time('mysql', true)
            ));
        }
        return self::get_winners($cid);
    }

    
    public static function eligible_count($competition_id) {
        global $wpdb;
        $tbl = self::table();
        return (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(DISTINCT cp.email) FROM {$tbl} cp
              WHERE cp.competition_id = %d AND cp.status = 'confirmed'
                AND NOT EXISTS (SELECT 1 FROM {$tbl} w WHERE w.email = cp.email AND w.is_winner = 1)",
            (int) $competition_id
        ));
    }

    
    public static function clear_winners($competition_id) {
        global $wpdb;
        $tbl = self::table();
        $wpdb->query($wpdb->prepare(
            "UPDATE {$tbl} SET is_winner = 0, won_at = NULL WHERE competition_id = %d AND is_winner = 1",
            (int) $competition_id
        ));
    }

    
    public static function set_winner($participant_id, $on) {
        global $wpdb;
        $tbl = self::table();
        $pid = (int) $participant_id;
        if ($on) {
            $wpdb->query($wpdb->prepare("UPDATE {$tbl} SET is_winner = 1, won_at = %s WHERE participant_id = %d", current_time('mysql', true), $pid));
        } else {
            $wpdb->query($wpdb->prepare("UPDATE {$tbl} SET is_winner = 0, won_at = NULL WHERE participant_id = %d", $pid));
        }
    }

    
    public static function get_winners($competition_id) {
        global $wpdb;
        $tbl = self::table();
        return (array) $wpdb->get_results($wpdb->prepare(
            "SELECT participant_id, email, first_name, last_name, won_at, status
               FROM {$tbl} WHERE competition_id = %d AND is_winner = 1
              ORDER BY won_at DESC, participant_id ASC",
            (int) $competition_id
        ));
    }

    

    
    public static function import_giveaways(array $args = array()) {
        if (function_exists('set_time_limit')) { @set_time_limit(0); }

        $dry          = !empty($args['dry_run']);
        $match        = array_key_exists('title_match', $args) ? (string) $args['title_match'] : 'giveaway';
        $only         = (isset($args['form_ids']) && is_array($args['form_ids'])) ? array_map('intval', $args['form_ids']) : array();
        $do_subscribe = !array_key_exists('subscribe', $args) || !empty($args['subscribe']);

        if (!class_exists('GFAPI') || !class_exists('AIEO_Competition_CPT')) {
            return array('ok' => false, 'error' => 'Gravity Forms or Competition CPT unavailable');
        }
        self::maybe_install();

        $forms  = array_merge((array) GFAPI::get_forms(true, false), (array) GFAPI::get_forms(false, false));
        $report = array(
            'ok' => true, 'dry_run' => $dry,
            'totals' => array('forms' => 0, 'entries' => 0, 'participants' => 0, 'verified' => 0,
                              'unique_emails' => 0, 'subscribed' => 0, 'sub_failed' => 0),
            'competitions' => array(),
            'errors'       => array(),
        );
        $global = array(); 

        foreach ($forms as $f) {
            $form_id = (int) ($f['id'] ?? 0);
            $title   = trim((string) ($f['title'] ?? ''));
            if ($form_id <= 0) { continue; }
            if ($only) { if (!in_array($form_id, $only, true)) { continue; } }
            elseif ($match !== '' && stripos($title, $match) === false) { continue; }

            list($start, $end, $count) = self::entry_date_range($form_id);
            $desc = trim((string) ($f['description'] ?? ''));

            
            $post_id = self::find_competition_post($form_id);
            if (!$dry) {
                if ($post_id <= 0) {
                    $ins = wp_insert_post(array(
                        'post_type'   => AIEO_Competition_CPT::POST_TYPE,
                        'post_status' => 'publish',
                        'post_title'  => $title !== '' ? $title : ('Competition #' . $form_id),
                    ), true);
                    if (is_wp_error($ins)) { $report['errors'][] = "form $form_id: " . $ins->get_error_message(); continue; }
                    $post_id = (int) $ins;
                } else {
                    wp_update_post(array('ID' => $post_id, 'post_title' => $title !== '' ? $title : ('Competition #' . $form_id)));
                }
                
                
                
                
                $ex_b  = (array) get_post_meta($post_id, AIEO_Competition_CPT::META_BRANDS, true);
                $ex_c  = (array) get_post_meta($post_id, AIEO_Competition_CPT::META_CATEGORIES, true);
                $ex_p  = (array) get_post_meta($post_id, AIEO_Competition_CPT::META_PRODUCTS, true);
                $ex_w  = (int) get_post_meta($post_id, AIEO_Competition_CPT::META_WINNERS_COUNT, true);
                $ex_pv = (float) get_post_meta($post_id, AIEO_Competition_CPT::META_PRIZE_VALUE, true);
                AIEO_Competition_CPT::persist_fields($post_id, $form_id, $desc, 0, 0, $start, $end, $ex_b, $ex_c, $ex_p, $ex_w, $ex_pv);
            }

            $cinfo = array('form_id' => $form_id, 'post_id' => $post_id, 'title' => $title,
                           'start' => $start, 'end' => $end, 'entries' => $count, 'participants' => 0, 'verified' => 0);

            
            $paging = array('offset' => 0, 'page_size' => 200);
            do {
                $entries = GFAPI::get_entries($form_id, array('status' => 'active'), null, $paging);
                if (is_wp_error($entries) || !is_array($entries)) { break; }
                foreach ($entries as $entry) {
                    list($email, $first, $last) = self::extract_email_name($entry, $f);
                    if ($email === '' || !is_email($email)) { continue; }
                    $resolved = self::resolve_eponymous_for_email($email);
                    $verified = self::is_verified_email($email, (int) $resolved['eponymous_id']);
                    $cinfo['participants']++;
                    if ($verified) { $cinfo['verified']++; }

                    if (!$dry && $post_id > 0) {
                        $now = current_time('mysql', true);
                        
                        
                        
                        $entry_date = !empty($entry['date_created']) ? (string) $entry['date_created'] : $now;
                        self::upsert_participant(array(
                            'competition_id' => (int) $post_id,
                            'gf_entry_id'    => (int) ($entry['id'] ?? 0),
                            'email'          => $email,
                            'first_name'     => $first,
                            'last_name'      => $last,
                            'status'         => 'confirmed',                  
                            'source'         => $verified ? 'customer' : 'import',
                            'eponymous_id'   => (int) $resolved['eponymous_id'],
                            'wp_user_id'     => (int) $resolved['wp_user_id'],
                            'created_at'     => $entry_date,
                            'confirmed_at'   => $now,
                        ));
                    }

                    if (!isset($global[$email])) {
                        $global[$email] = array($first, $last, $verified);
                    } else {
                        if ($verified) { $global[$email][2] = true; }
                        if ($first !== '' && $global[$email][0] === '') { $global[$email][0] = $first; }
                        if ($last  !== '' && $global[$email][1] === '') { $global[$email][1] = $last; }
                    }
                }
                $batch = count($entries);
                $paging['offset'] += $paging['page_size'];
            } while ($batch === $paging['page_size']);

            $report['totals']['forms']++;
            $report['totals']['entries']      += $cinfo['entries'];
            $report['totals']['participants'] += $cinfo['participants'];
            $report['totals']['verified']     += $cinfo['verified'];
            $report['competitions'][] = $cinfo;
        }

        
        $report['totals']['unique_emails'] = count($global);
        if ($do_subscribe && !$dry && !empty($global)) {
            if (!class_exists('AIEO_Newsletter_Registry') && defined('AIEO_DIR')) {
                $reg = AIEO_DIR . 'includes/dmm/push/class-aieo-newsletter-registry.php';
                if (file_exists($reg)) { require_once $reg; }
            }
            $adapter = class_exists('AIEO_Newsletter_Registry') ? AIEO_Newsletter_Registry::instance()->adapter() : null;
            if ($adapter && method_exists($adapter, 'subscribe_one')) {
                $i = 0;
                foreach ($global as $email => $info) {
                    
                    $opts = array('status' => 'confirmed', 'first_name' => $info[0], 'last_name' => $info[1], 'cohort' => self::LIST_COHORT);
                    $res = $adapter->subscribe_one($email, $opts);
                    if (is_array($res) && !empty($res['ok'])) {
                        $report['totals']['subscribed']++;
                        if (!empty($res['list_id']) && (int) get_option(self::LIST_OPTION, 0) <= 0) { update_option(self::LIST_OPTION, (int) $res['list_id'], false); }
                    } else {
                        $report['totals']['sub_failed']++;
                        if (count($report['errors']) < 10 && is_array($res) && !empty($res['error'])) {
                            $report['errors'][] = $email . ': ' . $res['error'];
                        }
                    }
                    if ((++$i % 50) === 0) { usleep(100000); } 
                }
            } else {
                $report['errors'][] = 'No TNP subscribe_one adapter — emails captured but not pushed.';
            }
        }

        return $report;
    }

    
    private static function entry_date_range($form_id) {
        global $wpdb;
        $t = $wpdb->prefix . 'gf_entry';
        $r = $wpdb->get_row($wpdb->prepare(
            "SELECT MIN(date_created) mn, MAX(date_created) mx, COUNT(*) n FROM {$t} WHERE form_id = %d AND status = 'active'",
            (int) $form_id
        ));
        if (!$r || (int) $r->n === 0) { return array('', '', 0); }
        
        return array(get_date_from_gmt((string) $r->mn), get_date_from_gmt((string) $r->mx), (int) $r->n);
    }

    
    private static function find_competition_post($form_id) {
        if (!class_exists('AIEO_Competition_CPT')) { return 0; }
        $ids = get_posts(array(
            'post_type'     => AIEO_Competition_CPT::POST_TYPE,
            'post_status'   => 'any',
            'numberposts'   => 1,
            'fields'        => 'ids',
            'no_found_rows' => true,
            'meta_key'      => AIEO_Competition_CPT::META_GF,
            'meta_value'    => (int) $form_id,
        ));
        return $ids ? (int) $ids[0] : 0;
    }
}
