<?php
defined('ABSPATH') || exit;

/**
 * Persists sync log entries to the {prefix}jra_merchant_log DB table.
 *
 * Table is created on plugin activation via JRA_Merchant_Plugin::activate().
 * All timestamps are stored in UTC.
 */
class JRA_Merchant_Logger {

    private const TABLE = 'jra_merchant_log';

    // -------------------------------------------------------------------------
    // Schema
    // -------------------------------------------------------------------------

    public static function createTable(): void {
        global $wpdb;
        $table   = $wpdb->prefix . self::TABLE;
        $charset = $wpdb->get_charset_collate();

        $sql = "CREATE TABLE IF NOT EXISTS {$table} (
            id         bigint(20)   NOT NULL AUTO_INCREMENT,
            created_at datetime     NOT NULL,
            sync_type  varchar(10)  NOT NULL DEFAULT '',
            status     varchar(10)  NOT NULL DEFAULT 'info',
            product_id bigint(20)   DEFAULT NULL,
            offer_id   varchar(255) DEFAULT NULL,
            message    text         NOT NULL,
            PRIMARY KEY  (id),
            KEY idx_created (created_at),
            KEY idx_status  (status)
        ) {$charset};";

        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        dbDelta($sql);
    }

    // -------------------------------------------------------------------------
    // Write methods
    // -------------------------------------------------------------------------

    public function info(string $message, string $syncType = '', ?int $productId = null, string $offerId = ''): void {
        $this->insert('info', $message, $syncType, $productId, $offerId);
    }

    public function warning(string $message, string $syncType = '', ?int $productId = null, string $offerId = ''): void {
        $this->insert('warning', $message, $syncType, $productId, $offerId);
    }

    public function error(string $message, string $syncType = '', ?int $productId = null, string $offerId = ''): void {
        $this->insert('error', $message, $syncType, $productId, $offerId);
        // Also write to PHP error log so it shows in server logs
        error_log('[JRA Merchant] ' . $message); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
    }

    // -------------------------------------------------------------------------
    // Read methods
    // -------------------------------------------------------------------------

    /**
     * @return object[]
     */
    public function getRecent(int $limit = 250, int $offset = 0, string $status = ''): array {
        global $wpdb;
        $table = $wpdb->prefix . self::TABLE;

        if ($status !== '' && in_array($status, ['info', 'warning', 'error'], true)) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            return (array) $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT * FROM {$table} WHERE status = %s ORDER BY id DESC LIMIT %d OFFSET %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
                    $status, $limit, $offset
                )
            );
        }

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        return (array) $wpdb->get_results(
            $wpdb->prepare(
                "SELECT * FROM {$table} ORDER BY id DESC LIMIT %d OFFSET %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
                $limit, $offset
            )
        );
    }

    public function countAll(string $status = ''): int {
        global $wpdb;
        $table = $wpdb->prefix . self::TABLE;

        if ($status !== '' && in_array($status, ['info', 'warning', 'error'], true)) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            return (int) $wpdb->get_var(
                $wpdb->prepare("SELECT COUNT(*) FROM {$table} WHERE status = %s", $status) // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            );
        }

        return (int) $wpdb->get_var("SELECT COUNT(*) FROM {$table}"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    }

    public function clear(): void {
        global $wpdb;
        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching -- TRUNCATE cannot use prepare()
        $wpdb->query('TRUNCATE TABLE ' . $wpdb->prefix . self::TABLE);
    }

    // -------------------------------------------------------------------------
    // Private
    // -------------------------------------------------------------------------

    private function insert(
        string $status,
        string $message,
        string $syncType,
        ?int   $productId,
        string $offerId
    ): void {
        global $wpdb;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->insert(
            $wpdb->prefix . self::TABLE,
            [
                'created_at' => current_time('mysql', true), // UTC
                'sync_type'  => substr($syncType, 0, 10),
                'status'     => $status,
                'product_id' => $productId,
                'offer_id'   => $offerId !== '' ? substr($offerId, 0, 255) : null,
                'message'    => $message,
            ],
            ['%s', '%s', '%s', $productId === null ? '%s' : '%d', $offerId !== '' ? '%s' : '%s', '%s']
        );
    }
}
