<?php

namespace AC_SYNC\Includes\Classes\Logger {
  if (!defined('ABSPATH')) exit; // Exit if accessed directly
  class Action_Wear_Log
  {

    const CRITICAL = 0;
    const ERROR = 1;
    const WARNING = 2;
    const INFO = 3;

    const CONTEXT_GENERAL = "GENERAL";
    const CONTEXT_PRODUCT_CREATION = "PRODUCT_CREATION";
    const CONTEXT_PRODUCT_UPDATE = "PRODUCT_UPDATE";
    const CONTEXT_CRONJOB = "CRONJOB";
    const CONTEXT_CAMAC_API = "CAMAC_API";

    const BUFFER_THRESHOLD = 50;

    private static $buffer = [];
    private static $enabledCache = null;

    /**
     * Scrive un log
     *
     * Accumula i log in un buffer e li scrive in batch al raggiungimento della soglia
     * o allo shutdown di PHP.
     *
     * @param string $msg Il messaggio da scrivere
     * @param self::CRITICAL|self::ERROR|self::WARNING|self::INFO|int $gravity Un numero che indica la gravità del log, disponibili costanti di classe
     * @param self::CONTEXT_GENERAL|self::CONTEXT_PRODUCT_CREATION|self::CONTEXT_PRODUCT_UPDATE|self::CONTEXT_CRONJOB|string $context Una stringa che identifica il contesto, preferibile usare le costanti di classi ma si possono mettere anche valori custom
     *
     * @return void
     **/
    public static function write(string $msg, $gravity = self::CRITICAL, $context = self::CONTEXT_GENERAL, $line = null, $file = null)
    {
      if (self::$enabledCache === null) {
        self::$enabledCache = get_option('_ACTIONWEAR_LOG_WRITING', 1) ? true : false;
      }
      if (!self::$enabledCache) return;
      if ($line !== null) $msg .= " in line $line";
      if ($file !== null) $msg .= " on file $file";

      self::$buffer[] = [
        "message" => $msg,
        "gravity" => $gravity,
        "context" => $context
      ];

      // Register shutdown flush on first write
      if (count(self::$buffer) === 1) {
        register_shutdown_function([__CLASS__, 'flush']);
      }

      if (count(self::$buffer) >= self::BUFFER_THRESHOLD) {
        self::flush();
      }
    }

    /**
     * Flush all buffered logs to the database in a single multi-row INSERT
     */
    public static function flush()
    {
      if (empty(self::$buffer)) return;
      global $wpdb;
      $table = $wpdb->prefix . "actionwear_log";
      $values = [];
      $placeholders = [];
      foreach (self::$buffer as $entry) {
        $placeholders[] = "(%s, %d, %s)";
        $values[] = $entry["message"];
        $values[] = $entry["gravity"];
        $values[] = $entry["context"];
      }
      $sql = "INSERT INTO $table (message, gravity, context) VALUES " . implode(", ", $placeholders);
      $wpdb->query($wpdb->prepare($sql, ...$values));
      self::$buffer = [];
    }

    public static function getLogs(int $limit = 10000)
    {
      self::flush();
      global $wpdb;
      $limit = (int) $limit;
      $logs = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$wpdb->prefix}actionwear_log ORDER BY id DESC LIMIT %d", $limit));
      return $logs;
    }

    public static function truncateLogs()
    {
      self::$buffer = [];
      global $wpdb;
      return $wpdb->query("DELETE FROM {$wpdb->prefix}actionwear_log");
    }

    /**
     * Delete logs older than N days. Runs at most once per day.
     *
     * @param int $days
     * @return int Number of deleted rows, or -1 if skipped
     */
    public static function pruneOldLogs(int $days = 30): int
    {
      $lastPrune = (int) get_option('_ACTIONWEAR_LAST_LOG_PRUNE', 0);
      if ((time() - $lastPrune) < DAY_IN_SECONDS) {
        return -1;
      }

      global $wpdb;
      $table = $wpdb->prefix . 'actionwear_log';

      $wpdb->query($wpdb->prepare(
        "DELETE FROM {$table} WHERE created_at < DATE_SUB(NOW(), INTERVAL %d DAY)",
        $days
      ));

      update_option('_ACTIONWEAR_LAST_LOG_PRUNE', time(), false);

      return (int) $wpdb->rows_affected;
    }
  }
}
