<?php
/**
 * Log writer – inserts rows into wp_skyemag_logs.
 *
 * Called by all sync paths (runner, bootstrap, hook triggers).
 *
 * @package SkyEmagSync
 */

namespace SkyEmagSync\Sync;

defined( 'ABSPATH' ) || exit;

class Logger {

    const ACTION_PRICE     = 'price';
    const ACTION_STOCK     = 'stock';
    const ACTION_BOTH      = 'price+stock';
    const ACTION_BOOTSTRAP = 'bootstrap';
    const ACTION_SKIP      = 'skip';
    const ACTION_SANITIZE  = 'sanitize';
    const ACTION_NOTIFY    = 'notify';
    const ACTION_AWB       = 'awb';

    const RESULT_SUCCESS = 'success';
    const RESULT_FAIL    = 'fail';
    const RESULT_SKIP    = 'skip';

    /**
     * Log a sync event.
     *
     * @param int    $product_id   WC product ID (0 for global events)
     * @param int    $variation_id WC variation ID (0 if not a variation)
     * @param string $action       One of the ACTION_ constants
     * @param string $result       One of the RESULT_ constants
     * @param string $message      Error or info message
     */
    public static function log(
        int    $product_id,
        int    $variation_id,
        string $action,
        string $result,
        string $message = ''
    ): void {
        global $wpdb;

        $wpdb->insert(
            $wpdb->prefix . 'skyemag_logs',
            [
                'created_at'   => current_time( 'mysql' ),
                'product_id'   => $product_id,
                'variation_id' => $variation_id,
                'action'       => substr( $action,  0, 20 ),
                'result'       => substr( $result,  0, 10 ),
                'message'      => $message,
            ],
            [ '%s', '%d', '%d', '%s', '%s', '%s' ]
        );
    }

    /**
     * Convenience wrappers.
     */
    public static function success( int $product_id, int $variation_id, string $action, string $msg = '' ): void {
        self::log( $product_id, $variation_id, $action, self::RESULT_SUCCESS, $msg );
    }

    public static function fail( int $product_id, int $variation_id, string $action, string $msg ): void {
        self::log( $product_id, $variation_id, $action, self::RESULT_FAIL, $msg );
    }

    public static function skip( int $product_id, int $variation_id, string $reason ): void {
        self::log( $product_id, $variation_id, self::ACTION_SKIP, self::RESULT_SKIP, $reason );
    }

    /**
     * Read logs for the Logs admin page.
     *
     * @param array $filters  Optional: product_id, result, action, limit, offset
     * @return array[] Rows as associative arrays.
     */
    public static function get_logs( array $filters = [] ): array {
        global $wpdb;

        $table  = $wpdb->prefix . 'skyemag_logs';
        $where  = [];
        $values = [];

        if ( ! empty( $filters['product_id'] ) ) {
            $where[]  = 'product_id = %d';
            $values[] = (int) $filters['product_id'];
        }
        if ( ! empty( $filters['result'] ) ) {
            $where[]  = 'result = %s';
            $values[] = $filters['result'];
        }
        if ( ! empty( $filters['action'] ) ) {
            $where[]  = 'action = %s';
            $values[] = $filters['action'];
        }

        $limit  = (int) ( $filters['limit']  ?? 100 );
        $offset = (int) ( $filters['offset'] ?? 0 );

        $where_sql = $where ? ' WHERE ' . implode( ' AND ', $where ) : '';
        $values[]  = $limit;
        $values[]  = $offset;

        $sql = "SELECT * FROM `{$table}`{$where_sql} ORDER BY id DESC LIMIT %d OFFSET %d";

        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare,WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $sql is built from $wpdb->prefix + a static suffix and a $where_sql clause that contains only %s placeholders (see lines 90-99); $values are bound by prepare()
        return $wpdb->get_results( $wpdb->prepare( $sql, $values ), ARRAY_A ) ?: [];
    }

    /**
     * Count total log rows (for pagination).
     */
    public static function count_logs( array $filters = [] ): int {
        global $wpdb;
        $table  = $wpdb->prefix . 'skyemag_logs';
        $where  = [];
        $values = [];

        if ( ! empty( $filters['result'] ) ) {
            $where[]  = 'result = %s';
            $values[] = $filters['result'];
        }

        $where_sql = $where ? ' WHERE ' . implode( ' AND ', $where ) : '';
        $sql       = "SELECT COUNT(*) FROM `{$table}`{$where_sql}";

        if ( $values ) {
            // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare,WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- same construction guarantees as read_logs() above
            return (int) $wpdb->get_var( $wpdb->prepare( $sql, $values ) );
        }

        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $sql is "SELECT COUNT(*) FROM `<prefix>skyemag_logs`" with no user input
        return (int) $wpdb->get_var( $sql );
    }

    /**
     * Prune logs older than $days days. Skips silently when the table
     * does not exist yet — fresh installs that haven't run the activation
     * hook (MU-plugin auto-load, manual upload before activate, partial
     * upgrades) will see the daily cron tick before Installer::activate
     * had a chance to create wp_skyemag_logs. Without this guard MySQL
     * raises a "table doesn't exist" error every 24h.
     */
    public static function prune( int $days = 30 ): void {
        global $wpdb;
        $table = $wpdb->prefix . 'skyemag_logs';

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- table existence probe before DELETE
        $exists = (string) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
        if ( $exists !== $table ) {
            return;
        }

        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- $table is $wpdb->prefix + a hard-coded suffix; $days is bound by prepare()
        $wpdb->query(
            $wpdb->prepare(
                "DELETE FROM `{$table}` WHERE created_at < DATE_SUB(NOW(), INTERVAL %d DAY)",
                $days
            )
        );
    }
}
