<?php
/**
 * Volade Gamification — XP, niveaux, badges, streaks
 *
 * @package Volade\SDK
 * @license GPL-2.0-or-later
 */

trait Volade_Gamification {

    private $xp_rules = array();
    private $badge_definitions = array();

    public function init_gamification() {
        $this->xp_rules = array(
            'login'        => 5,
            'register'     => 50,
            'daily_visit'  => 2,
            'install'      => 50,
            'referral'     => 100,
            'review'       => 30,
            'share'        => 20,
            'badge_earned' => 25,
            'onboarding_complete' => 30,
        );

        $this->badge_definitions = $this->build_badge_definitions();

        add_action( 'volade_daily_cron', array( $this, 'process_daily_streaks' ) );
        add_action( 'wp_ajax_volade_add_xp', array( $this, 'ajax_add_xp' ) );
    }

    /**
     * @return array<string, array{name:string,desc:string,icon:string,require:string,count?:int}>
     */
    private function build_badge_definitions() {
        return array(
            'early_bird'      => array( 'name' => __( 'Pionnier', 'php-compatibility-checker-by-volade' ), 'desc' => __( 'A rejoint Volade la première année.', 'php-compatibility-checker-by-volade' ), 'icon' => '🐦', 'require' => 'early_bird' ),
            'social'          => array( 'name' => __( 'Papillon social', 'php-compatibility-checker-by-volade' ), 'desc' => __( 'A parrainé 5 amis.', 'php-compatibility-checker-by-volade' ), 'icon' => '🦋', 'require' => 'referrals', 'count' => 5 ),
            'veteran'         => array( 'name' => __( 'Vétéran', 'php-compatibility-checker-by-volade' ), 'desc' => __( 'A atteint le niveau 10.', 'php-compatibility-checker-by-volade' ), 'icon' => '⭐', 'require' => 'level', 'count' => 10 ),
            'collector'       => array( 'name' => __( 'Collectionneur', 'php-compatibility-checker-by-volade' ), 'desc' => __( 'A installé 5 plugins Volade.', 'php-compatibility-checker-by-volade' ), 'icon' => '📦', 'require' => 'installs', 'count' => 5 ),
            'streak_7'        => array( 'name' => __( 'Guerrier hebdomadaire', 'php-compatibility-checker-by-volade' ), 'desc' => __( 'Série de 7 jours.', 'php-compatibility-checker-by-volade' ), 'icon' => '🔥', 'require' => 'streak', 'count' => 7 ),
            'streak_30'       => array( 'name' => __( 'Légende mensuelle', 'php-compatibility-checker-by-volade' ), 'desc' => __( 'Série de 30 jours.', 'php-compatibility-checker-by-volade' ), 'icon' => '💎', 'require' => 'streak', 'count' => 30 ),
            'referral_master' => array( 'name' => __( 'Maître du parrainage', 'php-compatibility-checker-by-volade' ), 'desc' => __( 'A parrainé 20 amis.', 'php-compatibility-checker-by-volade' ), 'icon' => '👑', 'require' => 'referrals', 'count' => 20 ),
        );
    }

    /**
     * @return array<string, array{name:string,desc:string,icon:string,require:string,count?:int}>
     */
    private function get_badge_definitions() {
        if ( empty( $this->badge_definitions ) ) {
            $this->badge_definitions = $this->build_badge_definitions();
        }
        return $this->badge_definitions;
    }

    public function get_badge_name( $badge_key ) {
        $key = sanitize_key( $badge_key );
        $def = $this->get_badge_definitions()[ $key ] ?? array();
        return $def['name'] ?? $badge_key;
    }

    public function enrich_badge( array $badge ) {
        $key = sanitize_key( $badge['badge_key'] ?? $badge['key'] ?? '' );
        if ( '' === $key ) {
            return $badge;
        }

        $def = $this->get_badge_definitions()[ $key ] ?? array();

        return array_merge(
            array(
                'key'       => $key,
                'badge_key' => $key,
            ),
            $def,
            $badge,
            $def
        );
    }

    public function add_xp( $user_id, $action, $extra = 0 ) {
        global $wpdb;

        $base = isset( $this->xp_rules[ $action ] ) ? $this->xp_rules[ $action ] : 0;
        if ( $base <= 0 ) {
            return null;
        }
        $total = $base + $extra;

        $user = $wpdb->get_row( $wpdb->prepare(
            "SELECT xp, level FROM {$wpdb->prefix}volade_users WHERE id = %d",
            $user_id
        ) );

        if ( ! $user ) {
            return;
        }

        $new_xp = $user->xp + $total;
        $new_level = $this->calculate_level( $new_xp );

        $wpdb->update(
            "{$wpdb->prefix}volade_users",
            array(
                'xp'         => $new_xp,
                'level'      => $new_level,
                'updated_at' => current_time( 'mysql' ),
            ),
            array( 'id' => $user_id )
        );

        if ( class_exists( 'Volade_SDK' ) ) {
            $sdk = Volade_SDK::get_instance();
            if ( $sdk && method_exists( $sdk, 'clear_user_data_cache' ) ) {
                $sdk->clear_user_data_cache( $user_id );
            }
        }

        $this->log_event( $user_id, 'xp_earned', array(
            'action' => $action,
            'amount' => $total,
            'total'  => $new_xp,
        ) );

        if ( $new_level > $user->level ) {
            $this->log_event( $user_id, 'level_up', array(
                'old_level' => $user->level,
                'new_level' => $new_level,
            ) );
        }

        $this->check_badges( $user_id );

        return array(
            'xp_earned'  => $total,
            'total_xp'   => $new_xp,
            'level'      => $new_level,
            'level_up'   => $new_level > $user->level,
        );
    }

    /**
     * Attribue un montant d'XP personnalisé (missions, bonus…).
     *
     * @param array<string, mixed> $event_data
     * @return array<string, mixed>|null
     */
    public function grant_xp( $user_id, $amount, $reason = 'mission', $event_data = array() ) {
        global $wpdb;

        $amount = (int) $amount;
        if ( $amount <= 0 ) {
            return null;
        }

        $user = $wpdb->get_row( $wpdb->prepare(
            "SELECT xp, level FROM {$wpdb->prefix}volade_users WHERE id = %d",
            $user_id
        ) );

        if ( ! $user ) {
            return null;
        }

        $new_xp    = $user->xp + $amount;
        $new_level = $this->calculate_level( $new_xp );

        $wpdb->update(
            "{$wpdb->prefix}volade_users",
            array(
                'xp'         => $new_xp,
                'level'      => $new_level,
                'updated_at' => current_time( 'mysql' ),
            ),
            array( 'id' => $user_id )
        );

        if ( class_exists( 'Volade_SDK' ) ) {
            $sdk = Volade_SDK::get_instance();
            if ( $sdk && method_exists( $sdk, 'clear_user_data_cache' ) ) {
                $sdk->clear_user_data_cache( $user_id );
            }
        }

        $this->log_event(
            $user_id,
            'xp_earned',
            array_merge(
                array(
                    'action' => $reason,
                    'amount' => $amount,
                    'total'  => $new_xp,
                ),
                $event_data
            )
        );

        if ( $new_level > $user->level ) {
            $this->log_event(
                $user_id,
                'level_up',
                array(
                    'old_level' => $user->level,
                    'new_level' => $new_level,
                )
            );
        }

        $this->check_badges( $user_id );

        return array(
            'xp_earned' => $amount,
            'total_xp'  => $new_xp,
            'level'     => $new_level,
            'level_up'  => $new_level > $user->level,
        );
    }

    public function process_daily_streaks() {
        global $wpdb;
        $yesterday = wp_date( 'Y-m-d', current_time( 'timestamp' ) - DAY_IN_SECONDS );
        $today     = current_time( 'Y-m-d' );

        $users = $wpdb->get_results(
            "SELECT id, streak, last_activity FROM {$wpdb->prefix}volade_users 
             WHERE last_activity IS NOT NULL"
        );

        foreach ( $users as $user ) {
            $last = wp_date( 'Y-m-d', strtotime( $user->last_activity ) );

            if ( $last === $today ) {
                continue;
            }

            if ( $last === $yesterday ) {
                $this->add_xp( $user->id, 'daily_visit' );
            } else {
                $wpdb->update(
                    "{$wpdb->prefix}volade_users",
                    array( 'streak' => 0, 'updated_at' => current_time( 'mysql' ) ),
                    array( 'id' => $user->id )
                );
            }
        }
    }

    public function record_activity( $user_id ) {
        global $wpdb;
        $today = current_time( 'Y-m-d' );

        $user = $wpdb->get_row( $wpdb->prepare(
            "SELECT last_activity, streak FROM {$wpdb->prefix}volade_users WHERE id = %d",
            $user_id
        ) );

        if ( ! $user ) {
            return;
        }

        $last = $user->last_activity ? wp_date( 'Y-m-d', strtotime( $user->last_activity ) ) : '';

        if ( $last === $today ) {
            return;
        }

        if ( $last === wp_date( 'Y-m-d', current_time( 'timestamp' ) - DAY_IN_SECONDS ) ) {
            $new_streak = $user->streak + 1;
        } else {
            $new_streak = 1;
        }

        $wpdb->update(
            "{$wpdb->prefix}volade_users",
            array(
                'last_activity' => current_time( 'mysql' ),
                'streak'        => $new_streak,
                'updated_at'    => current_time( 'mysql' ),
            ),
            array( 'id' => $user_id )
        );

        $this->add_xp( $user_id, 'daily_visit' );
        $this->check_badges( $user_id );
    }

    private function calculate_level( $xp ) {
        $xp = max( 0, (int) $xp );
        return max( 1, (int) floor( $xp / 100 ) + 1 );
    }

    /**
     * Aligne le niveau stocké sur l'XP réel (évite les écarts après migrations / tests).
     */
    public function sync_user_level( $user_id ) {
        global $wpdb;
        $xp = (int) $wpdb->get_var( $wpdb->prepare(
            "SELECT xp FROM {$wpdb->prefix}volade_users WHERE id = %d",
            $user_id
        ) );
        if ( ! $xp && 0 !== $xp ) {
            return;
        }
        $level = $this->calculate_level( $xp );
        $wpdb->update(
            "{$wpdb->prefix}volade_users",
            array( 'level' => $level, 'updated_at' => current_time( 'mysql' ) ),
            array( 'id' => $user_id )
        );
        if ( class_exists( 'Volade_SDK' ) ) {
            $sdk = Volade_SDK::get_instance();
            if ( $sdk && method_exists( $sdk, 'clear_user_data_cache' ) ) {
                $sdk->clear_user_data_cache( $user_id );
            }
        }
        return $level;
    }

    public function get_level_xp_requirements( $level ) {
        $level = max( 1, (int) $level );
        $xp_for_level = ( $level - 1 ) * 100;
        return array(
            'current'  => $xp_for_level,
            'next'     => $level * 100,
        );
    }

    public function get_user_badges( $user_id ) {
        global $wpdb;
        $rows = $wpdb->get_results( $wpdb->prepare(
            "SELECT badge_key, earned_at FROM {$wpdb->prefix}volade_badges WHERE user_id = %d",
            $user_id
        ), ARRAY_A );

        $badges = array();
        foreach ( $rows as $row ) {
            $badges[] = $this->enrich_badge(
                array(
                    'key'       => $row['badge_key'],
                    'badge_key' => $row['badge_key'],
                    'earned_at' => $row['earned_at'],
                )
            );
        }
        return $badges;
    }

    private function check_badges( $user_id ) {
        global $wpdb;
        $user = $wpdb->get_row( $wpdb->prepare(
            "SELECT level, streak, xp, created_at FROM {$wpdb->prefix}volade_users WHERE id = %d",
            $user_id
        ) );

        if ( ! $user ) {
            return;
        }

        $referral_count = $wpdb->get_var( $wpdb->prepare(
            "SELECT COUNT(*) FROM {$wpdb->prefix}volade_referrals WHERE referrer_id = %d AND status = 'converted'",
            $user_id
        ) );

        $plugin_count = (int) $wpdb->get_var( $wpdb->prepare(
            "SELECT COUNT(DISTINCT plugin_slug) FROM {$wpdb->prefix}volade_events WHERE user_id = %d AND event_type = 'plugin_active' AND plugin_slug != ''",
            $user_id
        ) );

        foreach ( $this->get_badge_definitions() as $key => $def ) {
            $has = $wpdb->get_var( $wpdb->prepare(
                "SELECT id FROM {$wpdb->prefix}volade_badges WHERE user_id = %d AND badge_key = %s",
                $user_id, $key
            ) );
            if ( $has ) {
                continue;
            }

            $earn = false;
            switch ( $def['require'] ?? '' ) {
                case 'level':
                    $earn = $user->level >= $def['count'];
                    break;
                case 'streak':
                    $earn = $user->streak >= $def['count'];
                    break;
                case 'referrals':
                    $earn = $referral_count >= $def['count'];
                    break;
                case 'installs':
                    $earn = $plugin_count >= $def['count'];
                    break;
                case 'early_bird':
                    $earn = $user->created_at && (int) gmdate( 'Y', strtotime( $user->created_at ) ) <= 2027;
                    break;
            }

            if ( $earn ) {
                $wpdb->insert(
                    "{$wpdb->prefix}volade_badges",
                    array(
                        'user_id'   => $user_id,
                        'badge_key' => $key,
                        'earned_at' => current_time( 'mysql' ),
                    )
                );
                $this->add_xp( $user_id, 'badge_earned' );
                $this->log_event( $user_id, 'badge_earned', array( 'badge_key' => $key ) );
            }
        }
    }

    private function log_event( $user_id, $event_type, $event_data = array(), $plugin_slug = '' ) {
        global $wpdb;
        $wpdb->insert(
            "{$wpdb->prefix}volade_events",
            array(
                'user_id'     => $user_id,
                'event_type'  => $event_type,
                'event_data'  => wp_json_encode( $event_data ),
                'plugin_slug' => $plugin_slug ?: ( $this->plugin_slug ?? '' ),
                'ip_address'  => $_SERVER['REMOTE_ADDR'] ?? '',
                'created_at'  => current_time( 'mysql' ),
            )
        );
    }

    public function ajax_add_xp() {
        check_ajax_referer( 'volade_nonce', 'nonce' );
        $user = $this->get_current_user();
        if ( ! $user ) {
            wp_send_json_error( 'Not authenticated' );
        }
        $action = sanitize_text_field( $_POST['action_name'] ?? '' );
        $result = $this->add_xp( $user['id'], $action );
        wp_send_json_success( $result );
    }

    public function rest_add_xp( $request ) {
        $user = $this->get_current_user();
        if ( ! $user ) {
            return new WP_REST_Response( array( 'error' => 'Not authenticated' ), 401 );
        }

        $rate_key = 'volade_xp_' . (int) $user['id'];
        $count    = (int) get_transient( $rate_key );
        if ( $count > 30 ) {
            return new WP_REST_Response( array( 'error' => 'Rate limit exceeded' ), 429 );
        }
        set_transient( $rate_key, $count + 1, HOUR_IN_SECONDS );

        $params = $request->get_params();
        $action = sanitize_key( $params['action'] ?? '' );
        $result = $this->add_xp( $user['id'], $action );
        if ( null === $result ) {
            return new WP_REST_Response( array( 'error' => 'Invalid action' ), 400 );
        }
        return new WP_REST_Response( $result, 200 );
    }

    public function get_all_badge_definitions() {
        return $this->get_badge_definitions();
    }

    public function track_plugin_active( $user_id, $plugin_slug ) {
        if ( ! $user_id || ! $plugin_slug ) {
            return;
        }
        global $wpdb;
        $seen = $wpdb->get_var( $wpdb->prepare(
            "SELECT id FROM {$wpdb->prefix}volade_events WHERE user_id = %d AND event_type = 'plugin_active' AND plugin_slug = %s LIMIT 1",
            $user_id,
            $plugin_slug
        ) );
        if ( $seen ) {
            return;
        }
        $this->log_event( $user_id, 'plugin_active', array( 'plugin' => $plugin_slug ), $plugin_slug );
        $this->check_badges( $user_id );
    }
}
