<?php
namespace OxygenApp\Models;

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

class User extends BaseModel {
    const TABLE_NAME = 'oxygen_app_users';

    private $table;

    public function __construct() {
        global $wpdb;
        $this->table = esc_sql($wpdb->prefix . self::TABLE_NAME);
    }

    /**
     * Create a new user
     */
    public function create($data) {
        global $wpdb;
        $data = $this->sanitize($data);

        $defaultData = [
            'email' => null,
            'password' => null,
            'activation_code' => wp_generate_password(30, false),
            'reset_token' => wp_generate_password(30, false),
            'is_active' => 0,
            'current_language' => 'en',
            'block_user' => 0,
            'last_login_time' => null,
            'terms' => null,
            'creation_date' => current_time('mysql', true),
            'meta' => ''
        ];

        $data = array_merge($defaultData, $data);
        
        $hashedPassword = password_hash($data['password'], PASSWORD_DEFAULT);
        $data['password'] = $hashedPassword;

        $format = [
            '%s',  // name
            '%s',  // email
            '%s',  // password
            '%s',  // activation_code
            '%s',  // reset_token
            '%d',  // is_active
            '%s',  // current_language
            '%d',  // block_user
            '%s',  // last_login_time
            '%s',  // terms
            '%s',  // creation_date
            '%s',  // meta
        ];

        $inserted = $wpdb->insert(
            $this->table,
            $data,
            $format
        );
        
        return $inserted ? $wpdb->insert_id : false;
    }

    /**
     * Update an existing user
     */
    public function update($id, $data) {
        global $wpdb;
    
        $data = $this->sanitize($data);
    
        if (isset($data['password'])) {
            $data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
        }
        
        $format = [];
        foreach ($data as $key => $value) {
            if (in_array($key, ['is_active', 'block_user'], true)) {
                $format[] = '%d'; // Integers
            } else {
                $format[] = '%s'; // Strings & Dates
            }
        }

        return $wpdb->update(
            $this->table,
            $data,
            ['id' => (int) $id],
            $format,
            ['%d']
        );
    }

    public function getUserMetaByKey(int $userId, string $metaKey, mixed $default = null): mixed {
        $meta = $this->getUserMeta($userId);

        return array_key_exists($metaKey, $meta) ? $meta[$metaKey] : $default;
    }

    public function getUserMeta(int $userId): mixed {
        global $wpdb;

        $table_name = esc_sql($wpdb->prefix . User::TABLE_NAME);

        $metaJson = $wpdb->get_var(
            $wpdb->prepare("SELECT meta FROM {$table_name} WHERE id = %d", $userId)
        );

        if (!$metaJson) {
            return [];
        }

        $meta = json_decode($metaJson, true);
        return is_array($meta) ? $meta : [];
    }

    public function updateUserMeta(int $userId, string $metaKey, mixed $metaValue): bool {
        global $wpdb;

        $meta = $this->getUserMeta($userId);
        if (!is_array($meta)) {
            $meta = [];
        }

        $metaValue = wp_kses_post_deep($metaValue);
        $metaKey = sanitize_key($metaKey);

        $meta[$metaKey] = $metaValue;

        $metaJson = json_encode($meta);

        $table_name = esc_sql($wpdb->prefix . User::TABLE_NAME);

        $result = $wpdb->update(
            $table_name,
            ['meta' => $metaJson],
            ['id' => $userId],
            ['%s'],
            ['%d']
        );

        return $result !== false;
    }
    
    public function findById($id) {
        global $wpdb;
        $id = absint($id);
        $table_name = esc_sql($wpdb->prefix . User::TABLE_NAME);
        $query = "SELECT * FROM {$table_name} WHERE id = %d";
        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
        return $wpdb->get_row($wpdb->prepare($query, $id), ARRAY_A);
    }

    public function find(string $field, mixed $fieldValue): ?array {
        global $wpdb;

        // Whitelist allowed columns and their formats
        $allowedFields = [
            'email'           => '%s',
            'id'              => '%d',
            'activation_code' => '%s',
            'reset_token'     => '%s',
        ];

        if (!isset($allowedFields[$field])) {
            return null;
        }

        $format = $allowedFields[$field];

        // Column name comes ONLY from whitelist (no escaping needed)
        $column = $field;

        // Prepare value safely
        $value = wp_unslash($fieldValue);

        return $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM {$this->table} WHERE {$column} = {$format}",
            $value
        ), ARRAY_A);
    }


    /**
     * Search users
     *
     * @return array Associative array with:
     * - data: Array of users matching the search criteria
     * - pagination: Array with current_page, per_page, total_results, total_pages
     */
    public function search(array $params = []) {
        global $wpdb;

        // Total count (no prepare needed)
        $total_results = (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$this->table}"
        );

        // Pagination values (sanitized in PHP)
        $per_page = isset($params['per_page']) ? absint($params['per_page']) : 20;
        $per_page = max(1, min($per_page, 100));

        $current_page = isset($params['page']) ? absint($params['page']) : 1;
        $current_page = max(1, $current_page);

        $total_pages = $total_results > 0
            ? (int) ceil($total_results / $per_page)
            : 1;

        if ($current_page > $total_pages) {
            $current_page = $total_pages;
        }

        $offset = ($current_page - 1) * $per_page;

        // Proper use of prepare with placeholders

        $results = $wpdb->get_results($wpdb->prepare(
            "SELECT *
            FROM {$this->table}
            ORDER BY creation_date DESC
            LIMIT %d OFFSET %d",
            $per_page,
            $offset
        ), ARRAY_A);

        return [
            'data' => $results,
            'pagination' => [
                'current_page' => $current_page,
                'per_page' => $per_page,
                'total_results' => $total_results,
                'total_pages' => $total_pages,
                'has_prev' => $current_page > 1,
                'has_next' => $current_page < $total_pages,
            ],
        ];
    }

}