<?php

class UM_User_Query_Engine {

    /**
     * Bump cache invalidation so directory results refresh after user data changes.
     */
    public static function flush_cache() {
        update_option( 'um_ue_query_cache_bump', (string) time(), false );
    }

    /**
     * @return string
     */
    private static function get_cache_bump() {
        return (string) get_option( 'um_ue_query_cache_bump', '0' );
    }

    /**
     * @param array $columns
     * @return array
     */
    private static function sanitize_search_columns( $columns ) {
        $allowed = [ 'user_login', 'user_nicename', 'user_email', 'display_name', 'user_url' ];
        $out     = [];
        foreach ( (array) $columns as $col ) {
            $col = sanitize_key( (string) $col );
            if ( in_array( $col, $allowed, true ) ) {
                $out[] = $col;
            }
        }
        return $out;
    }

    public static function get_users( $settings = [] ) {

        $settings = self::normalize_settings( $settings );

        $args = self::build_query_args( $settings );

        $cache_key = 'um_query_' . self::get_cache_bump() . '_' . md5( wp_json_encode( $args ) );
        $data      = get_transient( $cache_key );

        if ( $data === false ) {
            $query = new WP_User_Query( $args );

            $users = (array) $query->get_results();
            $total = (int) $query->get_total();

            if ( ! empty( $users ) ) {
                $user_ids = array_map( function ( $u ) {
                    return is_object( $u ) ? $u->ID : $u;
                }, $users );

                update_meta_cache( 'user', $user_ids );
            }

            $data = [
                'users' => $users,
                'total' => $total,
            ];

            set_transient( $cache_key, $data, 5 * MINUTE_IN_SECONDS );
        }

        return $data;
    }

    /**
     * Normalize and sanitize settings
     */
    private static function normalize_settings( $settings ) {

        $defaults = [
            'number'                    => 12,
            'users_per_page'            => 12,
            'user_numbers'              => 4,
            'paged'                     => 1,
            'search'                    => '',
            'search_columns'            => null,
            'roles'                     => [],
            'meta_filters'              => [],
            'orderby'                   => 'registered',
            'order'                     => 'DESC',
            'enable_pagination'         => 'no',
            'query_show_selected_roles' => 'no',
            'show_only_approved_users'  => 'no',
        ];

        $settings = wp_parse_args( $settings, $defaults );

        if ( ! empty( $settings['query_user_order_by'] ) ) {
            $settings['orderby'] = $settings['query_user_order_by'];
        }

        if ( isset( $settings['query_user_order'] ) && $settings['query_user_order'] !== '' && $settings['query_user_order'] !== null ) {
            $settings['order'] = $settings['query_user_order'];
        }

        if ( $settings['enable_pagination'] === 'yes' ) {
            $settings['number'] = max( 1, (int) $settings['users_per_page'] );
        } else {
            $settings['number'] = max( 1, (int) $settings['user_numbers'] );
            $settings['paged']  = 1;
        }

        $settings['paged'] = max( 1, (int) $settings['paged'] );
        $settings['order'] = strtoupper( $settings['order'] ) === 'ASC' ? 'ASC' : 'DESC';

        if ( ! empty( $settings['search'] ) ) {
            $settings['search'] = sanitize_text_field( (string) $settings['search'] );
        }

        if ( isset( $settings['search_columns'] ) && is_array( $settings['search_columns'] ) ) {
            $sanitized = self::sanitize_search_columns( $settings['search_columns'] );
            $settings['search_columns'] = ! empty( $sanitized ) ? $sanitized : null;
        } else {
            $settings['search_columns'] = null;
        }

        return $settings;
    }

    /**
     * Build safe WP_User_Query args
     */
    private static function build_query_args( $settings ) {

        $args = [
            'number'      => $settings['number'],
            'paged'       => $settings['paged'],
            'count_total' => true,
            'order'       => $settings['order'],
        ];

        $orderby = (string) ( $settings['orderby'] ?? 'registered' );

        switch ( $orderby ) {
            case 'display_name':
            case 'name':
                $args['orderby'] = 'display_name';
                break;
            case 'ID':
                $args['orderby'] = 'ID';
                break;
            case 'user_registered':
            case 'registered':
                $args['orderby'] = 'registered';
                break;
            case 'user_name':
                $args['orderby'] = 'login';
                break;
            case 'user_email':
                $args['orderby'] = 'user_email';
                break;
            case 'first_name':
            case 'last_name':
                $args['meta_key'] = $orderby;
                $args['orderby']  = 'meta_value';
                break;
            case 'post_count':
                $args['orderby'] = 'post_count';
                break;
            case 'last_login':
                $args['meta_key'] = 'last_login';
                $args['orderby']  = 'meta_value_num';
                break;
            case 'custom_field':
            case 'meta':
                if ( ! empty( $settings['user_query_by_meta_key'] ) ) {
                    $args['meta_key'] = sanitize_key( $settings['user_query_by_meta_key'] );
                    $args['orderby']  = 'meta_value';
                } else {
                    $args['orderby'] = 'registered';
                }
                break;
            default:
                $args['orderby'] = 'registered';
        }

        if ( $settings['query_show_selected_roles'] === 'yes' && ! empty( $settings['roles'] ) ) {
            $args['role__in'] = array_map( 'sanitize_key', (array) $settings['roles'] );
        }

        if ( ! empty( $settings['search'] ) ) {
            $search = $settings['search'];
            $args['search'] = '*' . $search . '*';
            if ( ! empty( $settings['search_columns'] ) && is_array( $settings['search_columns'] ) ) {
                $args['search_columns'] = $settings['search_columns'];
            } else {
                $args['search_columns'] = [
                    'user_login',
                    'user_nicename',
                    'user_email',
                    'display_name',
                ];
            }
        }

        $meta_query = [];

        if ( $settings['show_only_approved_users'] === 'yes' ) {
            $meta_query[] = [
                'key'     => 'account_status',
                'value'   => 'approved',
                'compare' => '=',
            ];
        }

        if ( ! empty( $settings['meta_query'] ) && is_array( $settings['meta_query'] ) ) {
            $allowed_compare = [
                '=',
                '!=',
                '>',
                '>=',
                '<',
                '<=',
                'LIKE',
                'NOT LIKE',
                'IN',
                'NOT IN',
                'BETWEEN',
                'NOT BETWEEN',
                'EXISTS',
                'NOT EXISTS',
            ];

            foreach ( $settings['meta_query'] as $mq ) {
                if ( empty( $mq['key'] ) || ! isset( $mq['value'] ) ) {
                    continue;
                }

                $compare = strtoupper( (string) ( $mq['compare'] ?? '=' ) );
                $value   = $mq['value'];

                if ( is_array( $value ) ) {
                    $value = array_map( 'sanitize_text_field', $value );
                } else {
                    $value = sanitize_text_field( $value );
                }

                $meta_query[] = [
                    'key'     => sanitize_key( $mq['key'] ),
                    'value'   => $value,
                    'compare' => in_array( $compare, $allowed_compare, true ) ? $compare : '=',
                ];
            }
        }

        if ( ! empty( $settings['meta_filters'] ) ) {
            $allowed_compare = [
                '=',
                '!=',
                '>',
                '>=',
                '<',
                '<=',
                'LIKE',
                'NOT LIKE',
                'IN',
                'NOT IN',
                'BETWEEN',
                'NOT BETWEEN',
                'EXISTS',
                'NOT EXISTS',
            ];

            foreach ( $settings['meta_filters'] as $filter ) {

                if ( empty( $filter['key'] ) || ! isset( $filter['value'] ) ) {
                    continue;
                }

                $compare = strtoupper( (string) ( $filter['compare'] ?? '=' ) );

                $meta_query[] = [
                    'key'     => sanitize_key( $filter['key'] ),
                    'value'   => sanitize_text_field( $filter['value'] ),
                    'compare' => in_array( $compare, $allowed_compare, true ) ? $compare : '=',
                ];
            }
        }

        if ( ! empty( $meta_query ) ) {
            $args['meta_query'] = $meta_query;
        }

        return $args;
    }
}

add_action( 'profile_update', [ 'UM_User_Query_Engine', 'flush_cache' ], 10, 0 );
add_action( 'set_user_role', [ 'UM_User_Query_Engine', 'flush_cache' ], 10, 0 );
add_action( 'user_register', [ 'UM_User_Query_Engine', 'flush_cache' ], 10, 0 );
add_action( 'delete_user', [ 'UM_User_Query_Engine', 'flush_cache' ], 10, 0 );

add_action(
    'updated_user_meta',
    static function ( $meta_id, $user_id, $meta_key ) {
        $relevant = [ 'account_status', 'first_name', 'last_name', 'country', 'city', 'gender', 'role' ];
        if ( in_array( $meta_key, $relevant, true ) || str_starts_with( (string) $meta_key, 'um_' ) ) {
            UM_User_Query_Engine::flush_cache();
        }
    },
    10,
    3
);
