<?php

/**
 * Classe Metabox
 *
 * Essa classe é usada para criar metaboxes em um sistema WordPress.
 * Um metabox é uma caixa de metadados que pode ser adicionada a uma postagem, página ou outro tipo de conteúdo.
 *
 * @package O2M
 */
class Metabox {

  /**
   * Define se o metabox deve ser exibido ou não.
   *
   * @var bool
   */
  private $should_display;

  /**
   * ID único do metabox.
   *
   * @var string
   */
  private $id;

  /**
   * Título do metabox.
   *
   * @var string
   */
  private $title;

  /**
   * Função de retorno de chamada (callback) para renderizar o conteúdo do metabox.
   *
   * @var mixed
   */
  private $callback;

  /**
   * Tela(s) onde o metabox será exibido.
   * Pode ser uma string representando o nome de uma tela ou um array de nomes de telas.
   * Também pode ser nulo para exibir em todas as telas.
   *
   * @var mixed
   */
  private $screen;

  /**
   * Contexto em que o metabox será exibido.
   * Pode ser 'normal', 'advanced' ou 'side'.
   *
   * @var string
   */
  private $context;

  /**
   * Prioridade de exibição do metabox.
   * Pode ser 'default', 'high', 'low' ou 'core'.
   *
   * @var string
   */
  private $priority;

  /**
   * Campos do metabox.
   *
   * @var array
   */
  private $fields;

  /**
   * Construtor da classe Metabox.
   *
   * @param string $id               ID único do metabox.
   * @param string $title            Título do metabox.
   * @param mixed  $callback         Função de retorno de chamada (callback) para renderizar o conteúdo do metabox.
   * @param mixed  $screen           Tela(s) onde o metabox será exibido.
   * @param string $context          Contexto em que o metabox será exibido.
   * @param string $priority         Prioridade de exibição do metabox.
   * @param bool   $should_display   Define se o metabox deve ser exibido ou não.
   */
  public function __construct(
    $id,
    $title,
    $callback,
    $screen,
    $context = 'normal',
    $priority = 'default',
    $should_display = true
  ) {
    $this->id = $id;
    $this->title = $title;
    $this->callback = $callback;
    $this->screen = $screen;
    $this->context = $context;
    $this->priority = $priority;
    $this->should_display = $should_display;
    $this->fields = [];

    // Adiciona ação para chamar o método add_meta_box() quando os meta boxes forem adicionados
    add_action('add_meta_boxes', array($this, 'add_meta_box'));

    // Adiciona ação para chamar o método save() quando o post for salvo
    add_action('save_post', array($this, 'save'));
  }

  /**
   * Define se o metabox deve ser exibido ou não.
   *
   * @param bool $should_display   Valor que define se o metabox deve ser exibido ou não.
   */
  public function set_should_display($should_display) {
    $this->should_display = $should_display;
  }

  /**
   * Obtém se o metabox deve ser exibido ou não.
   *
   * @return bool   Valor que define se o metabox deve ser exibido ou não.
   */
  public function get_should_display() {
    return $this->should_display;
  }

  /**
   * Define o ID do metabox.
   *
   * @param string $id   ID único do metabox.
   */
  public function set_id($id) {
    $this->id = $id;
  }

  /**
   * Obtém o ID do metabox.
   *
   * @return string   ID único do metabox.
   */
  public function get_id() {
    return $this->id;
  }

  /**
   * Define o título do metabox.
   *
   * @param string $title   Título do metabox.
   */
  public function set_title($title) {
    $this->title = $title;
  }

  /**
   * Obtém o título do metabox.
   *
   * @return string   Título do metabox.
   */
  public function get_title() {
    return $this->title;
  }

  /**
   * Define a função de retorno de chamada (callback) para renderizar o conteúdo do metabox.
   *
   * @param mixed $callback   Função de retorno de chamada (callback).
   */
  public function set_callback($callback) {
    $this->callback = $callback;
  }

  /**
   * Obtém a função de retorno de chamada (callback) do metabox.
   *
   * @return mixed   Função de retorno de chamada (callback).
   */
  public function get_callback() {
    return $this->callback;
  }

  /**
   * Define a tela(s) onde o metabox será exibido.
   *
   * @param mixed $screen   Tela(s) onde o metabox será exibido.
   */
  public function set_screen($screen) {
    $this->screen = $screen;
  }

  /**
   * Obtém a tela(s) onde o metabox será exibido.
   *
   * @return mixed   Tela(s) onde o metabox será exibido.
   */
  public function get_screen() {
    return $this->screen;
  }

  /**
   * Define o contexto em que o metabox será exibido.
   *
   * @param string $context   Contexto em que o metabox será exibido.
   */
  public function set_context($context) {
    $this->context = $context;
  }

  /**
   * Obtém o contexto em que o metabox será exibido.
   *
   * @return string   Contexto em que o metabox será exibido.
   */
  public function get_context() {
    return $this->context;
  }

  /**
   * Define a prioridade de exibição do metabox.
   *
   * @param string $priority   Prioridade de exibição do metabox.
   */
  public function set_priority($priority) {
    $this->priority = $priority;
  }

  /**
   * Obtém a prioridade de exibição do metabox.
   *
   * @return string   Prioridade de exibição do metabox.
   */
  public function get_priority() {
    return $this->priority;
  }

  /**
   * Define os campos do metabox.
   *
   * @param array $fields   Campos do metabox.
   */
  public function set_fields($fields) {
    $this->fields = $fields;
  }

  /**
   * Obtém os campos do metabox.
   *
   * @return array   Campos do metabox.
   */
  public function get_fields() {
    return $this->fields;
  }

  /**
   * Adiciona um campo ao metabox.
   *
   * @param string $key   Chave do campo a ser adicionado.
   */
  public function add_field($key) {
    $this->fields[] = $key;
  }

  /**
   * Método chamado quando os metaboxes são adicionados.
   * Adiciona o metabox com base nas configurações definidas.
   */
  public function add_meta_box() {
    if (!$this->should_display) {
      return;
    }

    // Adiciona o metabox usando a função add_meta_box() do WordPress
    add_meta_box(
      $this->id,                // ID único do metabox
      $this->title,             // Título do metabox
      array($this, 'render'),   // Função de callback para renderizar o conteúdo do metabox
      $this->screen,            // Tela(s) onde o metabox será exibido
      $this->context,           // Contexto em que o metabox será exibido
      $this->priority           // Prioridade de exibição do metabox
    );
  }

  /**
   * Método de callback para renderizar o conteúdo do metabox.
   * Chama a função de callback definida e renderiza os campos do metabox.
   */
  public function render() {
    MetaboxController::set_metabox($this);

    echo '<div class="o2-metabox">';

    // Chama a função de callback para renderizar o conteúdo do metabox
    call_user_func($this->callback);

    // Renderiza o campo oculto para armazenar os valores dos campos
    echo Input::render(array(
      'name'  => $this->id,
      'value' => implode(',', $this->fields),
      'type'  => 'hidden'
    ));

    echo '</div>';
  }

  /**
   * Método chamado quando um post é salvo.
   * Salva os valores dos campos do metabox como metadados do post.
   *
   * @param int $post_id   ID do post sendo salvo.
   */
  public function save($post_id) {
    if (!empty($_POST[$this->id])) {
      $fields = explode(',', $_POST[$this->id] ?? array());

      // Itera sobre os campos e atualiza os metadados do post
      foreach ($fields as $key) {
        if (!empty($_POST[$key])) {
          update_post_meta($post_id, $key, $_POST[$key]);
        } else {
          delete_post_meta($post_id, $key);
        }
      }
    }
  }
}
