<?php

namespace PostTypes;

/**
 * A Metabox in ACF is called a "Field Group" or just "Group".
 * So all instances of "Metabox" or "Box" can be understood as an "ACF Field Group"
 *
 * @see https://github.com/AdvancedCustomFields/acf
 */
class Metabox
{
    /**
     * The Box's key
     */
    public string $key;

    /**
     * The Box's title
     * they $key is derived from that
     */
    public string $title;

    /**
     * ACF location rule groups
     */
    public array $locations = [];

    /**
     * The Box's fields to add
     *
     * @var MetaboxField[]
     */
    public array $fields = [];

    /**
     * The "ACF Group Settings"
     * @see https://www.advancedcustomfields.com/resources/register-fields-via-php/#group-settings
     */
    public array $options = [];

    /**
     * Metabox constructor.
     *
     * @param string $title
     * @param array  $options
     */
    public function __construct(string $title, array $options = [])
    {
        // @see https://github.com/AdvancedCustomFields/acf/blob/5.9.3/includes/local-fields.php#L163
        $this->key = 'group_' . str_replace(['_', '-', '/', ' '], '_', strtolower($title));
        $this->title = $title;

        $this->options($options);
    }

    /**
     * Assign a PostType to add the Metabox to
     *
     * @param  string|array $posttypes
     *
     * @return $this
     */
    public function posttype($posttypes)
    {
        $posttypes = (array)$posttypes;

        foreach ($posttypes as $posttype) {
            $this->location('post_type', $posttype);
        }

        return $this;
    }

    /**
     * Assign a Taxonomy to add the Metabox to
     *
     * @param  string|array $taxonomies
     *
     * @return $this
     */
    public function taxonomy($taxonomies)
    {
        $taxonomies = (array)$taxonomies;

        foreach ($taxonomies as $taxonomy) {
            $this->location('taxonomy', $taxonomy);
        }

        return $this;
    }

    /**
     * Add a rule group to the acf group location
     *
     * @param string $param    The type to check against
     * @param string $value    The value to check against
     * @param string $operator The operator to check with
     *
     * @return $this
     */
    public function location(string $param, string $value, string $operator = '==')
    {
        $this->locations[] = compact('param', 'operator', 'value');

        return $this;
    }

    /**
     * @param MetaboxField|array $fields
     *
     * @return $this
     */
    public function field($fields)
    {
        if ($fields instanceof MetaboxField) {
            $fields = [$fields];
        }

        foreach ($fields as &$field) {
            // set the field's parent
            $field->parent($this->key);

            $this->fields[$field->key] = &$field;
        }

        return $this;
    }

    /**
     * @param array $options
     *
     * @return $this
     */
    public function options(array $options)
    {
        $this->options = $options;

        return $this;
    }

    /**
     * Add the acf local field group
     */
    public function add()
    {
        // build the location rule groups
        $ruleGroups = array_map(function ($location) {
            return [$location];
        }, $this->locations);

        // merge with extra options
        $fieldGroup = array_replace_recursive([
            'key' => $this->key,
            'title' => $this->title,
            'location' => $ruleGroups,
        ], $this->options);

        // add field group
        acf_add_local_field_group($fieldGroup);

        // add fields
        foreach ($this->fields as $field) {
            $field->add();
        }
    }
}