<?php

/**
 * Management of custom post types for Lockee
 */
class Lockee_Post_Types extends Lockee_Singleton
{
  /**
   * Meta fields configuration for the lock post type
   */
  private $meta_fields;
  private $slug_base;

  /**
   * Initialize the post types
   */
  protected function init()
  {
    $this->meta_fields = array(
      'lockee_type' => array(
        'type' => 'string',
      ),
      'lockee_code' => array(
        'type' => 'string',
      ),
      'lockee_options' => array(
        'type' => 'string',
      ),
      'lockee_test' => array(
        'type' => 'number',
        'default' => 0,
      ),
      'lockee_open' => array(
        'type' => 'number',
        'default' => 0,
      )
    );
    $this->slug_base = get_option('lockee_slug_base', 'locks');
  }

  /**
   * Auth callback for edit_posts capability
   */
  public function auth_callback_edit_posts()
  {
    return current_user_can('edit_posts');
  }

  /**
   * Add a noindex meta tag for locks
   */
  public function add_noindex_meta()
  {
    if (is_singular('lockee_lock')) {
      echo '<meta name="robots" content="noindex, nofollow" />' . "\n";
    }
  }

  /**
   * Customise the permalink structure for the lock post type
   */
  public function custom_post_type_link($post_link, $post)
  {
    if ($post->post_type === 'lockee_lock') {
      return home_url($this->slug_base . '/' . $post->ID . '/');
    }
    return $post_link;
  }

  /**
   * Register the custom post type for locks
   */
  public function register_post_types()
  {
    $menu_icon = file_get_contents(dirname(plugin_dir_path(__FILE__)) . '/images/icon.svg');
    register_post_type(
      'lockee_lock',
      array(
        'labels' => array(
          'menu_name' => __('Lockee', 'lockee'),
          'name' => __('Locks', 'lockee'),
          'singular_name' => __('Lock', 'lockee'),
          'all_items' => __('Locks', 'lockee'),
          'add_new' => __('Add new', 'lockee'),
          'add_new_item' => __('Add a new lock', 'lockee'),
          'edit' => __('Edit', 'lockee'),
          'edit_item' => __('Edit the lock', 'lockee'),
          'new_item' => __('New lock', 'lockee'),
          'view' => __('View lock', 'lockee'),
          'view_item' => __('View lock', 'lockee'),
          'search_items' => __('Search a lock', 'lockee'),
          'not_found' => __('No lock found', 'lockee'),
          'not_found_in_trash' => __('No lock found in trash', 'lockee'),
          'parent' => __('Parent of lock', 'lockee'),
        ),
        'supports' => array('title', 'editor', 'custom-fields'),
        'public' => true,
        'show_in_rest' => true,
        'rest_base' => $this->slug_base,
        'show_in_search' => false,
        'has_archive' => false,
        'hierarchical' => false,
        'exclude_from_search' => true,
        'rewrite' => array(
          'slug' => $this->slug_base,
          'with_front' => true,
          'pages' => true,
          'feeds' => false,
        ),
        'menu_position' => 20,
        'menu_icon' => 'data:image/svg+xml;base64,' . base64_encode($menu_icon),
      )
    );

    // Register all meta fields in a loop
    foreach ($this->meta_fields as $meta_key => $args) {
      $args['show_in_rest'] = true;
      $args['single'] = true;
      $args['auth_callback'] = array($this, 'auth_callback_edit_posts');
      register_post_meta('lockee_lock', $meta_key, $args);
    }

    // Add admin columns
    $this->add_admin_column('lockid', 'lockee_lock', function ($post_id) {
      echo $post_id;
    }, 'ID', true);

    $this->add_admin_column('attempts', 'lockee_lock', function ($post_id) {
      echo get_post_meta($post_id, 'lockee_test', true);
    }, 'lockee_test', true);

    $this->add_admin_column('openings', 'lockee_lock', function ($post_id) {
      echo get_post_meta($post_id, 'lockee_open', true);
    }, 'lockee_open', true);

    $this->add_admin_column('success', 'lockee_lock', function ($post_id) {
      $test_count = get_post_meta($post_id, 'lockee_test', true);
      $open_count = get_post_meta($post_id, 'lockee_open', true);
      if ($test_count > 0) {
        $percentage = round(($open_count / $test_count) * 100, 1);
        echo esc_html($percentage) . '%';
      } else {
        echo '0%';
      }
    }, 'lockee_success', false);
  }

  /**
   * Add custom rewrite rules for the lock post type
   */
  private function add_rewrite_rules()
  {
    add_rewrite_rule(
      $this->slug_base . '/([0-9]+)/?$',
      'index.php?post_type=lockee_lock&p=$matches[1]',
      'top'
    );

    add_rewrite_rule(
      $this->slug_base . '/([0-9]+)/iframe/?$',
      'index.php?post_type=lockee_lock&p=$matches[1]&view=iframe',
      'top'
    );
  }

  /**
   * Add an admin column to the lock post type
   */
  private function add_admin_column($column_title, $post_type, $cb, $order_by = false, $order_by_field_is_meta = false)
  {

    add_filter('manage_' . $post_type . '_posts_columns', function ($columns) use ($column_title) {
      $new_columns = array();
      foreach ($columns as $key => $value) {
        if ($key === 'date') {
          $new_columns[sanitize_title($column_title)] = $this->get_translate_title($column_title);
        }
        $new_columns[$key] = $value;
      }
      return $new_columns;
    });

    add_action('manage_' . $post_type . '_posts_custom_column', function ($column, $post_id) use ($column_title, $cb) {
      if (sanitize_title($column_title) === $column)
        $cb($post_id);
    }, 10, 2);

    if (!empty($order_by)) {
      add_filter('manage_edit-' . $post_type . '_sortable_columns', function ($columns) use ($column_title, $order_by) {
        $columns[sanitize_title($column_title)] = $order_by;
        return $columns;
      });

      if ($order_by === 'lockee_success') {
        add_filter('posts_clauses', function ($clauses, $wp_query) {
          global $wpdb;
          if (
            is_admin() &&
            $wp_query->is_main_query() &&
            $wp_query->get('post_type') === 'lock' &&
            $wp_query->get('orderby') === 'lockee_success'
          ) {
            $clauses['join'] .= " LEFT JOIN {$wpdb->postmeta} AS pm_test ON ({$wpdb->posts}.ID = pm_test.post_id AND pm_test.meta_key = 'lockee_test')";
            $clauses['join'] .= " LEFT JOIN {$wpdb->postmeta} AS pm_open ON ({$wpdb->posts}.ID = pm_open.post_id AND pm_open.meta_key = 'lockee_open')";
            $clauses['orderby'] = "ORDER BY (IFNULL(pm_open.meta_value,0) / NULLIF(IFNULL(pm_test.meta_value,0),0)) " . ($wp_query->get('order') === 'asc' ? 'ASC' : 'DESC');
          }
          $wp_query->set('orderby', null);
          $wp_query->set('meta_key', null);
          return $clauses;
        }, 10, 2);
      } else if ($order_by === 'ID') {
        add_filter('posts_clauses', function ($clauses, $wp_query) use ($order_by) {
          $wp_query->set('orderby', 'ID');
          $wp_query->set('meta_key', null);
          return $clauses;
        }, 10, 2);
      } else {
        if ($order_by_field_is_meta) {
          add_filter('posts_clauses', function ($clauses, $wp_query) use ($order_by) {
            global $wpdb;
            if (
              is_admin() &&
              $wp_query->is_main_query() &&
              $wp_query->get('post_type') === 'lock' &&
              $wp_query->get('orderby') === $order_by
            ) {
              $alias = 'pm_' . $order_by;
              if (strpos($clauses['join'], $alias) === false) {
                $clauses['join'] .= " LEFT JOIN {$wpdb->postmeta} AS $alias ON ({$wpdb->posts}.ID = $alias.post_id AND $alias.meta_key = '" . esc_sql($order_by) . "')";
              }
              $clauses['orderby'] = "ORDER BY CAST($alias.meta_value AS UNSIGNED) " . ($wp_query->get('order') === 'asc' ? 'ASC' : 'DESC');
            }
            $wp_query->set('orderby', null);
            $wp_query->set('meta_key', null);
            return $clauses;
          }, 10, 2);
        } else {
          add_action('pre_get_posts', function ($query) use ($order_by) {
            if (!is_admin() || !$query->is_main_query())
              return;
            if (sanitize_key($order_by) === $query->get('orderby')) {
              $query->set('orderby', sanitize_key($order_by));
            }
          });
        }
      }
    }
  }

  /**
   * Add styles for the custom columns in the admin interface
   */
  public function add_columns_styles()
  {
    echo '<style>
        .wp-list-table .column-lockid { width: 9%; }
        .wp-list-table .column-attempts { width: 9%; }
        .wp-list-table .column-openings { width: 9%; }
        .wp-list-table .column-success { width: 9%; }
    </style>';
  }

  /**
   * Translate the column title for the admin interface
   */
  private function get_translate_title($title)
  {
    switch ($title) {
      case 'lockid':
        return __('ID', 'lockee');
      case 'attempts':
        return __('Attempts', 'lockee');
      case 'openings':
        return __('Openings', 'lockee');
      case 'success':
        return __('Success', 'lockee');
      default:
        return $title;
    }
  }

  /**
   * Restrict access to the REST API for the lock post type
   */
  public function restrict_rest_access($result)
  {
    if (true === $result || is_wp_error($result)) {
      return $result;
    }

    if (isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], '/wp-json/wp/v2/' . $this->slug_base) !== false) {
      if (!is_user_logged_in() || (!current_user_can('manage_options') && !current_user_can('edit_posts') && !current_user_can('publish_posts'))) {
        return new WP_Error(
          'rest_forbidden',
          __('Sorry, you are not allowed to access this endpoint.', 'lockee'),
          array('status' => rest_authorization_required_code())
        );
      }
    }

    return $result;
  }

  /**
   * Update the rewrite rules for the lock post type
   */
  public function update_rewrite_rules()
  {
    $this->slug_base = get_option('lockee_slug_base', 'locks');
    $this->register_post_types();
    $this->add_rewrite_rules();
    flush_rewrite_rules();
  }

  public static function migrate_lock_to_lockee_lock()
  {
    global $wpdb;
    $wpdb->query(
      "UPDATE {$wpdb->posts} SET post_type = 'lockee_lock' WHERE post_type = 'lock'"
    );
    update_option('lockee_post_type_updated', '1');
  }

  public static function maybe_migrate_lock_to_lockee_lock()
  {
    if (get_option('lockee_post_type_updated') === '1') {
      return;
    }
    $args = array(
      'post_type' => 'lock',
      'posts_per_page' => 1,
      'post_status' => 'any',
      'fields' => 'ids',
    );
    $query = new WP_Query($args);
    if ($query->have_posts()) {
      self::migrate_lock_to_lockee_lock();
    } else {
      update_option('lockee_post_type_updated', '1');
    }
  }

  public static function update_rewrite_rules_static()
  {
    self::get_instance()->update_rewrite_rules();
  }
}
