<?php

declare(strict_types=1);

namespace Instinctor\Core\DB;

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

/**
 * WordPressDriver — delegates every DB operation to $wpdb.
 *
 * $wpdb is fetched via `global $wpdb` inside each method, not stored on the
 * instance. This avoids stale-reference issues if $wpdb is replaced or
 * re-initialised during WP's load sequence.
 */
final class WordPressDriver implements DriverInterface
{
    public function prepare(string $query, mixed ...$args): string
    {
        global $wpdb;
        // phpcs:disable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Instinctor DB abstraction passthrough to wpdb; query construction + parameterization is the caller's responsibility (this is the abstraction boundary).
        return $wpdb->prepare($query, ...$args);
        // phpcs:enable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL,PluginCheck.Security.DirectDB.UnescapedDBParameter
    }

    public function get_results(string $query, string $output = 'OBJECT'): array
    {
        global $wpdb;
        // phpcs:disable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Instinctor DB abstraction passthrough to wpdb; query construction + parameterization is the caller's responsibility (this is the abstraction boundary).
        return $wpdb->get_results($query, $output) ?? [];
        // phpcs:enable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL,PluginCheck.Security.DirectDB.UnescapedDBParameter
    }

    public function get_row(string $query, string $output = 'OBJECT', int $y = 0): mixed
    {
        global $wpdb;
        // phpcs:disable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Instinctor DB abstraction passthrough to wpdb; query construction + parameterization is the caller's responsibility (this is the abstraction boundary).
        return $wpdb->get_row($query, $output, $y);
        // phpcs:enable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL,PluginCheck.Security.DirectDB.UnescapedDBParameter
    }

    public function get_var(string $query, int $x = 0, int $y = 0): ?string
    {
        global $wpdb;
        // phpcs:disable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Instinctor DB abstraction passthrough to wpdb; query construction + parameterization is the caller's responsibility (this is the abstraction boundary).
        return $wpdb->get_var($query, $x, $y);
        // phpcs:enable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL,PluginCheck.Security.DirectDB.UnescapedDBParameter
    }

    public function get_col(string $query, int $x = 0): array
    {
        global $wpdb;
        // phpcs:disable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Instinctor DB abstraction passthrough to wpdb; query construction + parameterization is the caller's responsibility (this is the abstraction boundary).
        return $wpdb->get_col($query, $x) ?? [];
        // phpcs:enable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL,PluginCheck.Security.DirectDB.UnescapedDBParameter
    }

    public function query(string $query): int|false
    {
        global $wpdb;
        // phpcs:disable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Instinctor DB abstraction passthrough to wpdb; query construction + parameterization is the caller's responsibility (this is the abstraction boundary).
        $result = $wpdb->query($query);
        // phpcs:enable WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL,PluginCheck.Security.DirectDB.UnescapedDBParameter
        return $result === false ? false : (int) $result;
    }

    public function insert(string $table, array $data, ?array $format = null): int|false
    {
        global $wpdb;
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.SlowDBQuery -- direct query against the plugin's own custom table; the WP options/post API does not model this data and the read is request-scoped (object caching would add complexity without benefit)
        $result = $wpdb->insert($table, $data, $format);
        return $result === false ? false : (int) $result;
    }

    public function update(
        string $table,
        array $data,
        array $where,
        ?array $format = null,
        ?array $where_format = null
    ): int|false {
        global $wpdb;
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.SlowDBQuery -- direct query against the plugin's own custom table; the WP options/post API does not model this data and the read is request-scoped (object caching would add complexity without benefit)
        $result = $wpdb->update($table, $data, $where, $format, $where_format);
        return $result === false ? false : (int) $result;
    }

    public function delete(string $table, array $where, ?array $where_format = null): int|false
    {
        global $wpdb;
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.SlowDBQuery -- direct query against the plugin's own custom table; the WP options/post API does not model this data and the read is request-scoped (object caching would add complexity without benefit)
        $result = $wpdb->delete($table, $where, $where_format);
        return $result === false ? false : (int) $result;
    }

    public function replace(string $table, array $data, ?array $format = null): int|false
    {
        global $wpdb;
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.SlowDBQuery -- direct query against the plugin's own custom table; the WP options/post API does not model this data and the read is request-scoped (object caching would add complexity without benefit)
        $result = $wpdb->replace($table, $data, $format);
        return $result === false ? false : (int) $result;
    }

    public function prefix(): string
    {
        global $wpdb;
        return $wpdb->prefix;
    }

    public function insert_id(): int
    {
        global $wpdb;
        return (int) $wpdb->insert_id;
    }

    public function last_error(): string
    {
        global $wpdb;
        return (string) $wpdb->last_error;
    }

    public function esc_like(string $text): string
    {
        global $wpdb;
        return $wpdb->esc_like($text);
    }

    public function get_charset_collate(): string
    {
        global $wpdb;
        return $wpdb->get_charset_collate();
    }

    public function table(string $name): string
    {
        global $wpdb;
        return $wpdb->$name ?? $wpdb->prefix . $name;
    }
}
