<?php

namespace ModernFields\ImportExport\Services;

use WP_Post;

defined('ABSPATH') || exit;

/**
 * Serializes selected field groups, CPT definitions, taxonomy definitions and
 * block definitions into an array suitable for JSON export.
 *
 * @since 0.8
 */
class ExportService
{
  /**
   * Builds the full export payload for the given item IDs.
   *
   * @since 0.8
   * @param int[] $group_ids     IDs of field groups to export.
   * @param int[] $post_type_ids IDs of CPT definitions to export.
   * @param int[] $taxonomy_ids  IDs of taxonomy definitions to export.
   * @param int[] $block_ids     IDs of block definitions to export.
   * @return array
   */
  public function export(array $group_ids, array $post_type_ids, array $taxonomy_ids, array $block_ids = []): array
  {
    return [
      'mf_version'  => MODERN_FIELDS_VERSION,
      'exported_at' => gmdate('c'),
      'groups'      => $this->exportGroups($group_ids),
      'post_types'  => $this->exportDefinitions($post_type_ids, MODERN_FIELDS_POST_TYPES_CPT_SLUG),
      'taxonomies'  => $this->exportDefinitions($taxonomy_ids, MODERN_FIELDS_TAXONOMIES_CPT_SLUG),
      'blocks'      => $this->exportDefinitions($block_ids, MODERN_FIELDS_BLOCKS_CPT_SLUG),
    ];
  }

  /**
   * Serializes a list of field group posts.
   *
   * @since 0.8
   * @param int[] $ids
   * @return array
   */
  protected function exportGroups(array $ids): array
  {
    if (empty($ids)) {
      return [];
    }

    $posts = get_posts([
      'post_type'      => MODERN_FIELDS_CPT_SLUG,
      'post_status'    => ['publish', 'draft'],
      'posts_per_page' => -1,
      'include'        => $ids,
    ]);

    return array_values(array_map([$this, 'serializeGroup'], $posts));
  }

  /**
   * Serializes a single field group post to an exportable array.
   *
   * Generates a UUID on the fly if the group does not yet have one
   * (migration path for groups created before 0.7.2).
   *
   * @since 0.8
   * @param WP_Post $post
   * @return array
   */
  protected function serializeGroup(WP_Post $post): array
  {
    $uuid = get_post_meta($post->ID, 'mf_uuid', true);

    if (empty($uuid)) {
      $uuid = wp_generate_uuid4();
      update_post_meta($post->ID, 'mf_uuid', $uuid);
    }

    return [
      'uuid'        => $uuid,
      'title'       => $post->post_title,
      'description' => $post->post_excerpt,
      'content'     => $post->post_content,
      'status'      => $post->post_status,
      'modified'    => get_post_modified_time('c', true, $post->ID),
      'meta'        => [
        'mf_location' => (string) get_post_meta($post->ID, 'mf_location', true),
        'mf_scope'    => (string) get_post_meta($post->ID, 'mf_scope', true),
        'mf_targets'  => (array) get_post_meta($post->ID, 'mf_targets', false),
        'mf_roles'    => (array) get_post_meta($post->ID, 'mf_roles', false),
        'mf_settings' => (array) get_post_meta($post->ID, 'mf_settings', true),
        'mf_cpt'      => (string) get_post_meta($post->ID, 'mf_cpt', true),
        'mf_position' => (string) get_post_meta($post->ID, 'mf_position', true),
      ],
    ];
  }

  /**
   * Serializes CPT or taxonomy definition posts.
   *
   * @since 0.8
   * @param int[]  $ids
   * @param string $post_type CPT slug to query.
   * @return array
   */
  protected function exportDefinitions(array $ids, string $post_type): array
  {
    if (empty($ids)) {
      return [];
    }

    $posts = get_posts([
      'post_type'      => $post_type,
      'post_status'    => 'publish',
      'posts_per_page' => -1,
      'include'        => $ids,
    ]);

    return array_values(array_map([$this, 'serializeDefinition'], $posts));
  }

  /**
   * Serializes a single CPT or taxonomy definition post.
   *
   * @since 0.8
   * @param WP_Post $post
   * @return array
   */
  protected function serializeDefinition(WP_Post $post): array
  {
    $uuid = get_post_meta($post->ID, 'mf_uuid', true);

    if (empty($uuid)) {
      $uuid = wp_generate_uuid4();
      update_post_meta($post->ID, 'mf_uuid', $uuid);
    }

    return [
      'uuid'     => $uuid,
      'title'    => $post->post_title,
      'config'   => $post->post_content ? (array) json_decode($post->post_content, true) : [],
      'modified' => get_post_modified_time('c', true, $post->ID),
    ];
  }
}
