<?php

namespace ModernFields\FieldGroups\Repositories;

use ModernFields\FieldGroups\Entities\Attachment;
use ModernFields\FieldGroups\Entities\FieldGroup;
use ModernFields\FieldGroups\Entities\Post;
use ModernFields\FieldGroups\Entities\User;
use ModernFields\FieldGroups\Factories\MediaQueryFactory;
use ModernFields\FieldGroups\Factories\PostQueryFactory;
use ModernFields\FieldGroups\Factories\TermQueryFactory;
use ModernFields\FieldGroups\Factories\UserQueryFactory;

use WP_Post;

/**
 * Abstract class for field group repositories.
 * 
 * This class is responsible for retrieving field groups from the database.
 * 
 * @since 1.0.0
 */
abstract class FieldGroupRepository
{
  /**
   * Retrieve field group by ID.
   *
   * @since 1.0.0
   * @param int $id The ID of the field group to retrieve.
   * @return FieldGroup|null The field group object if found, null otherwise.
   */
  public static function getFieldGroupById(int $id): ?FieldGroup
  {
    $field_group = get_post($id);

    if (!$field_group) {
      return null;
    }

    if ($field_group->post_type !== MODERN_FIELDS_CPT_SLUG) {
      throw new \Exception('Invalid field group ID');
    }

    return new FieldGroup($field_group);
  }

  /**
   * Retrieve groups for Posts.
   *
   * @since 1.0.0
   * @param Post $post The post to retrieve field groups for.
   * @return FieldGroup[]
   */
  public static function getPostsFieldGroups(Post $post): array
  {
    return (new PostQueryFactory($post))
      ->getAllPostsQuery()
      ->getSpecificPostsQuery()
      ->getTemplateQuery()
      ->getHomepageQuery()
      ->getBlogHomeQuery()
      ->getTaxonomyQuery()
      ->getStatusQuery()
      ->executeQueries();
  }

  /**
   * Retrieve groups for new posts.
   *
   * @since 1.0.0
   * @param string $post_type The post type to retrieve field groups for.
   * @return FieldGroup[]
   */
  public static function getNewPostFieldGroups(string $post_type): array
  {
    # Create a new post object with only the post type
    $wp_post = new WP_Post((object) [
      'ID' => 0,
      'post_type' => $post_type,
    ]);

    $post = new Post($wp_post);

    return (new PostQueryFactory($post))
      ->getAllPostsQuery()
      ->executeQueries();
  }

  /**
   * Retrieve groups for Options Pages.
   *
   * @since 1.0.0
   * @return FieldGroup[]
   */
  public static function getOptionsPageFieldGroups(): array
  {
    $meta_query = [
      'relation' => 'AND',
      [
        'key' => 'mf_location',
        'value' => 'options-page',
        'compare' => '='
      ],
      [
        'key' => 'mf_roles',
        'value' => wp_get_current_user()->roles,
        'compare' => 'IN'
      ]
    ];

    $args = [
      'post_type' => MODERN_FIELDS_CPT_SLUG,
      'post_status' => 'publish',
      'posts_per_page' => -1,
      'meta_query' => $meta_query
    ];

    return array_map(fn($post) => new FieldGroup($post), get_posts($args));
  }

  /**
   * Retrieve groups for the admin Dashboard.
   *
   * No role filtering here: dashboard groups save their values as site
   * options, so access is gated on manage_options by DashboardLocation.
   *
   * @since 1.3.0
   * @return FieldGroup[]
   */
  public static function getDashboardFieldGroups(): array
  {
    $args = [
      'post_type' => MODERN_FIELDS_CPT_SLUG,
      'post_status' => 'publish',
      'posts_per_page' => -1,
      'meta_query' => [
        [
          'key' => 'mf_location',
          'value' => 'dashboard',
          'compare' => '='
        ]
      ]
    ];

    return array_map(fn($post) => new FieldGroup($post), get_posts($args));
  }

  /**
   * Retrieve every group whose values are stored as site options: options
   * pages and dashboard widgets. Used by consumers that read option-backed
   * fields regardless of where they are edited, like the Field Value block
   * "Options page" source.
   *
   * @since 1.3.0
   * @return FieldGroup[]
   */
  public static function getOptionsBackedFieldGroups(): array
  {
    return array_merge(
      self::getOptionsPageFieldGroups(),
      self::getDashboardFieldGroups()
    );
  }

  /**
   * Retrieve a field group by its ID.
   *
   * @since 1.0.0
   * @param int $group_id The ID of the field group to retrieve.
   * @return FieldGroup|null The field group object if found, null otherwise.
   */
  public static function getOptionsPageFieldGroupById(int $group_id): ?FieldGroup
  {
    $post = get_post($group_id);
    if (!$post) {
      return null;
    }
    return new FieldGroup($post);
  }

  /**
   * Retrieve field groups applicable to the given Media (Attachment) page.
   *
   * Finds all field groups registered for the 'media' location type. 
   * This may later be filtered by user or attachment context.
   *
   * @since 1.0.0
   * @param Attachment $attachment The attachment entity representing the current media item.
   * @return FieldGroup[] Array of matching FieldGroup objects.
   */
  public static function getMediaFieldGroups(Attachment $attachment): array
  {
    return (new MediaQueryFactory($attachment))
      ->getAllMediaQuery()
      ->getSpecificMediaTypeQuery()
      ->executeQueries();
  }

  /**
   * Retrieve groups for User Pages.
   *
   * @since 1.0.0
   * @param User $user The user to retrieve field groups for.
   * @return FieldGroup[]
   */
  public static function getUserFieldGroups(User $user): array
  {
    return (new UserQueryFactory($user))
      ->getAllUserQuery()
      ->getSpecificUserQuery()
      ->getSpecificRoleQuery()
      ->executeQueries();
  }

  /**
   * Retrieve groups for new users.
   *
   * @since 1.0.0
   * @return FieldGroup[]
   */
  public static function getNewUserFieldGroups(): array
  {
    return (new UserQueryFactory())
      ->getAllUserQuery()
      ->executeQueries();
  }

  /**
   * Retrieve all user field groups.
   *
   * @since 1.0.0
   * @return FieldGroup[]
   */
  public static function getAllFieldGroups(): array
  {
    $args = [
      'post_type' => MODERN_FIELDS_CPT_SLUG,
      'post_status' => 'publish',
      'posts_per_page' => -1,
    ];

    return array_map(fn($post) => new FieldGroup($post), get_posts($args));
  }

  /**
   * Retrieve groups for Blocks.
   *  
   * @since 1.0.0
   * @return FieldGroup[]
   */
  public static function getBlocksFieldGroups(): array
  {
    $meta_query = [
      'relation' => 'AND',
      [
        'key' => 'mf_location',
        'value' => 'block',
        'compare' => '='
      ]
    ];

    $args = [
      'post_type' => MODERN_FIELDS_CPT_SLUG,
      'post_status' => 'publish',
      'posts_per_page' => -1,
      'meta_query' => $meta_query
    ];

    return array_map(fn($post) => new FieldGroup($post), get_posts($args));
  }

  /**
   * Retrieve groups for Taxonomy Terms.
   *
   * @since 1.0.0
   * @param string $taxonomy The taxonomy string to retrieve field groups for.
   * @param User|null $user The user to check permissions for (optional).
   * @return FieldGroup[]
   */
  public static function getTermFieldGroups(string $taxonomy): array
  {
    return (new TermQueryFactory($taxonomy))
      ->getAllTaxonomiesQuery()
      ->getSpecificTaxonomyQuery()
      ->executeQueries();
  }
}
