<?php

namespace Eventman\Includes\Post;

use Eventman\Admin\LogTrait;
use plainview\sdk_broadcast\form2\inputs\traits\low;
use threewp_broadcast\api\api;
use threewp_broadcast\broadcast_data\blog;
use threewp_broadcast\maintenance\checks\container;

abstract class CustomPostType
{

    use LogTrait {
        log as _log;
    }

    protected function log($msg)
    {
        $this->_log((new \ReflectionClass($this))->getShortName() . ': ' . $msg);
    }

    /**
     * @var string
     */
    protected $identifier = '';

    /**
     * @var string
     */
    protected $menuIcon = 'dashicons-admin-post';

    /**
     * @var array
     */
    protected $fields = [];

    /**
     * @var array
     */
    protected $extraOptions;


    /**
     * CustomPostType constructor.
     * @param string $identifier post type name.
     * @param array $extraOptions options for custom post type.
     */
    public function __construct(string $identifier, array $extraOptions = [])
    {
        $this->identifier = $identifier;
        $this->extraOptions = $extraOptions;
    }

    public static function allStatuses()
    {
        return ['publish', 'pending', 'draft', 'auto-draft', 'future', 'private', 'inherit', 'trash'];
    }

    /**
     * Save custom meta fields to the database.
     * @param $postId int|string ID of the CPT
     * @return mixed
     * @throws \ReflectionException
     */
    public function saveMetaFieldsToDatabase($postId ) {
        global $wp_current_filter;

        $this->log('Saving metadata for post ' . $postId);

        if (empty($_POST) // post data is empty
            || in_array('wp_ajax_inline-save', $wp_current_filter)) { // is quick edit
            $this->log('POST body is empty, or save is via AJAX.');
            return $postId;
        }

        // verify nonce
        $name = strtolower((new \ReflectionClass($this))->getShortName());
        if ( isset( $_POST[$name . '_meta_box']) && !wp_verify_nonce( $_POST[$name . '_meta_box'], basename(__FILE__) ) ) {
            $this->log('Nonce invalid, not saving.');
            return $postId;
        }
        // check autosave
        if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
            return $postId;
        }
        // check permissions
        if ( isset($_POST['post_type'] ) && 'page' === $_POST['post_type'] ) {
            if ( !current_user_can( 'edit_page', $postId ) ) {
                return $postId;
            } elseif ( !current_user_can( 'edit_post', $postId ) ) {
                return $postId;
            }
        }

        // diff the custom meta data
        $old = get_post_meta($postId, 'custom_post_fields', true);
        $new = $_POST['custom_post_fields'] ?? [];


        $new['description'] = wp_specialchars_decode($new['description'] ?? '');

        $this->log('Fixing meta: ' . $this->pretty($new));

        $socialPrefix = 'social_';
        foreach($new as $fieldName => $field) {
            if (strpos($fieldName, $socialPrefix) !== false) {
                $siteBase = explode("_", $fieldName)[1] . '.com/';
                if (substr($fieldName, strlen($socialPrefix)) === 'linkedin') {
                    $siteBase .= 'in/';
                }
                if (strpos($field, $siteBase) !== false) {
                    $new[$fieldName] = substr($field, strpos($field, $siteBase) + strlen($siteBase));
                }
            } elseif (strcasecmp($fieldName, 'website') === 0 && strpos($field, 'http') === 0) {
                $colonIndex = strpos($field, ':');
                $new[$fieldName] = substr($field, $colonIndex + 3);
            }
        }
        $this->log('Fixed meta: ' . $this->pretty($new));


        if ( $new && $new !== $old ) {
            $this->log('Updating meta to database for post ' . $postId);
            update_post_meta( $postId, 'custom_post_fields', $new );
        } elseif ( '' === $new && $old ) {
            $this->log('Deleting meta to database for post ' . $postId);
            delete_post_meta( $postId, 'custom_post_fields', $old );
        }

        return $postId;
    }

    /**
     * Add a meta data field.
     *
     * $name should be a snake_cased field name.
     * $options should have a 'type' entry, valued with an HTML input type, or 'user'.
     *
     *
     * @param string $name
     * @param array $options
     * @return bool
     */
    public function addField(string $name, array $options)
    {
        if (!isset($options['type'])) {
            return false;
        }

        $this->fields[$name] = $options;
        return true;
    }

    /**
     * Generate HTML input control fields for wordpress.
     * @return string
     * @throws \ReflectionException
     */
    public function generateFormFields()
    {
        global $post;
        // get the post's relevant metadata
        $meta = get_post_meta( $post->ID, 'custom_post_fields', true );
        $classNameLowercase = strtolower((new \ReflectionClass($this))->getShortName());
        // add type nonce identifier
        $out = sprintf('<input type="hidden" name="%s_meta_box" value="%s">', $classNameLowercase, wp_create_nonce(basename(__FILE__)));

        // iterate over fields
        foreach ($this->fields as $name => $field) {
            // skip field if 'type' key is not set, or if 'user' is the field's type and the current user is not an admin
            if (!isset($field['type']) ||
                (strcasecmp($field['type'], 'user') == 0 && !in_array('administrator', wp_get_current_user()->roles))) {
                continue;
            }

            // get existing data
            $val = isset($meta[$name]) ? $meta[$name] : '';

            // generate attributes
            $lowerName = strtolower($name);
            $id = 'custom_post_fields[' . $lowerName . ']';

            // generate social media fields
            $socialPrefix = 'social_';
            if (substr($name, strlen($socialPrefix)) == $socialPrefix) {
                $name = substr($name, strpos($name, explode("_", $name)[1]));
                if ($name == 'linkedin') {
                    $name = 'linkedIn';
                }
            }
            $label = isset($field['label']) ? $field['label'] : str_replace("_", " ", ucfirst($name));

            // init temporary output
            $tmp = sprintf('<p class="field-row"><label class="cpt-lbl" for="%s">%s</label>', $id, _x($label, 'ms-eventman'));

            // generate html matching the field type
            if (strcasecmp($field['type'], 'text') == 0) {
                // create a text field
                $tmp .= sprintf('<input type="text" name="%s" id="%s" class="regular-text cpt-input" value="%s" />', $id, $id, $val);
            } elseif (strcasecmp($field['type'], 'textarea') == 0) {
                // create multiline text area
                ob_start();
                wp_editor( htmlspecialchars_decode($val), $id, [
                    'textarea_name' => $id,
                    'media_buttons' => false
                ]);
                $tmp .= ob_get_contents();
                ob_clean();
                // create checkbox
            } elseif (strcasecmp($field['type'], 'checkbox') == 0) {
                $tmp .= sprintf('<input type="checkbox" name="%s" id="%s" class="regular-text cpt-input" value="%s" />', $id, $id, $val);
            } elseif (strcasecmp($field['type'], 'select') == 0
                && isset($field['options']) && is_array($field['options']) && sizeof($field['options']) > 0) {
                // create select box
                $tmp .= sprintf('<select id="%s" name="%s" class="cpt-input">', $id, $id);
                foreach ($field['options'] as $option) {
                    $tmp .= sprintf('<option value="%s" %s>%s</option>', strtolower($option), strtolower($option) == $val ? 'selected' : '', $option);
                }
                $tmp .= '</select>';
            } else {
                // skip if no valid type
                continue;
            }

            $out .= $tmp . "</p>";
        }

        echo $out;
        return $out;
    }

    /**
     * Add the class meta to wordpress.
     */
    public function addMetaBox()
    {
        $name = strtolower((new \ReflectionClass($this))->getShortName());
        add_meta_box(
            $name . '_fields_box',
            'Fields',
            [$this, 'generateFormFields'],
            $name . 's',
            'normal',
            'default'
        );
    }

    /**
     * Register this type to wordpress.
     */
    public function register()
    {
        $name = _x((new \ReflectionClass($this))->getShortName(), 'ms-eventman');
        $lowerName = strtolower($name);

        // set create_posts to do_not_allow on main site
        $createPosts = "create_{$lowerName}s";
        if (!is_main_site()) {
            $createPosts = 'do_not_allow';
        }

        $plural = strtolower(_n((new \ReflectionClass($this))->getShortName(), _x((new \ReflectionClass($this))->getShortName() . 's', 'ms-eventman'), 2));

        $args = [
            'labels'            => $this->generateLabels(),
            'menu_icon'         => $this->menuIcon,
            'public'            => true,
            'has_archive'       => true,
            'rewrite'           => [
                'slug' => $plural,
                'with_front' => false
            ],
            'supports'          => [
                'title',
                'revisions',
                'author'
            ],
            'query_var' => true,
            'show_ui' => true,
            'publicly_queryable' => true,
            'capability_type' => ['post', $lowerName.'s', $lowerName],
            'capabilities'      => [
                // primitive caps used inside of map_meta_cap()
                'read'                   => 'read',
                'delete_posts'           => "manage_{$lowerName}s",
                'delete_private_posts'   => "manage_{$lowerName}s",
                'delete_published_posts' => "manage_{$lowerName}s",
                'delete_others_posts'    => "manage_{$lowerName}s",
                'edit_private_posts'     => "manage_{$lowerName}s",
                'create_posts' => $createPosts,
                'edit_post' => 'edit_' . $lowerName,
                'edit_posts' => 'edit_' . $lowerName . 's',
                'edit_others_posts' => 'edit_other_' . $lowerName . 's',
                'edit_published_posts' => 'edit_published_' . $lowerName . 's',
                'publish_posts' => 'publish_' . $lowerName . 's',
                'read_post' => 'read_' . $lowerName,
                'read_private_posts' => 'read_private_' . $lowerName . 's',
                'delete_post' => 'delete_' . $lowerName
            ],
            'map_meta_cap' => true,
            'show_in_menu' => true,
            'exclude_from_search' => false,
        ];
        $args = array_merge($args, $this->extraOptions);
        $caps = [
            $createPosts,
            'edit_' . $lowerName . 's',
            'edit_other_' . $lowerName . 's',
            'edit_published_posts' => 'edit_published_' . $lowerName . 's',
            'publish_' . $lowerName . 's',
            'read_private_' . $lowerName . 's',
            "manage_{$lowerName}s"
        ];
        if ($createPosts !== 'do_not_allow') {
            $caps[] = $createPosts;
        }
        $roles = [get_role('administrator')/*, get_role('editor')*/];
        foreach ($caps as $cap) {
            foreach($roles as $role) {
                $role->add_cap($cap);
            }
        }
        register_post_type($this->identifier, $args);
    }

    /**
     * Generate labels for this type to insert into the registration.
     * @param string $singular
     * @return array of labels
     */
    public function generateLabels()
    {
        $singular = _x((new \ReflectionClass($this))->getShortName(), 'ms-eventman');
        $plural = _n((new \ReflectionClass($this))->getShortName(), _x((new \ReflectionClass($this))->getShortName() . 's', 'ms-eventman'), 2);

        return [
            'name' =>               _x($plural, "ms-eventman"),
            'singular_name' =>      _x($singular, "ms-eventman"),
            'add_new' =>            _x( 'Add New ' . $singular, "ms-eventman"),
            'add_new_item' =>       _x( 'Add New ' . $singular, "ms-eventman" ),
            'edit_item' =>          _x( 'Edit ' . $singular, "ms-eventman" ),
            'new_item' =>           _x( 'Add New ' . $singular, "ms-eventman" ),
            'view_item' =>          _x( 'View ' . $singular, "ms-eventman" ),
            'search_items' =>       _x( 'Search ' . $singular, "ms-eventman" ),
            'not_found' =>          _x( 'No ' . strtolower($plural) . ' found', "ms-eventman" ),
            'not_found_in_trash' => _x( 'No ' . strtolower($singular) . ' found in trash', "ms-eventman" )
        ];
    }

    /**
     * @param int $blog_id
     * @return int[]|\WP_Post[]
     * @throws \ReflectionException
     */
    public static function getPostsForBlog(int $blog_id)
    {
        $name = strtolower((new \ReflectionClass(get_called_class()))->getShortName());
        if ($blog_id == get_current_blog_id()) {

            return get_posts(['post_type' => $name]);
        }
        switch_to_blog($blog_id);
        $posts = get_posts(['post_type' => $name]);
        restore_current_blog();
        return $posts;
    }

    /**
     * @return array|object|null
     */
    public static function getPostsForAllBlogs()
    {
        $siteIds = array_map(
            function($s) {
                return $s->blogId;
            }, get_sites());
        $allPosts = [];
        $name = strtolower((new \ReflectionClass(get_called_class()))->getShortName());
        foreach ($siteIds as $siteId) {
            switch_to_blog($siteId);
            $allPosts[$siteId] = get_posts([
                'post_type' => $name . 's',
                'post_status' => array_filter(
                    static::allStatuses(),
                    function($s) {
                        return $s != 'trash';
                    })
            ]);
            restore_current_blog();
        }

        return $allPosts;
    }

    /**
     * @return array|object|null
     */
    public static function getBroadcastedPostsForAllBlogs() {
        $postSelects = static::getPostSubqueriesForAllBlogs();
        $queryString = 'SELECT bp.blog_id, bp.post_id FROM (' . implode(' UNION ', $postSelects) . ') as bp JOIN wp__3wp_broadcast_broadcastdata as br ON br.blog_id = bp.blog_id AND br.post_id = bp.post_id';

        global $wpdb;
        $broadcastedExhibitors = $wpdb->getResults($queryString);
        $wpdb->flush();
        return $broadcastedExhibitors;
    }

    private static function getPostSubqueriesForAllBlogs()
    {
        $siteIds = array_map(
            function($s) {
                return $s->blogId;
            }, get_sites());
        return array_map(function($id) {
            $name = strtolower((new \ReflectionClass(get_called_class()))->getShortName());
            $tableName = $id == 1 ? 'wp_posts' : 'wp_' . $id . '_posts';
            return '(SELECT ' . $id . ' AS blog_id, ID as post_id FROM ' . $tableName . ' WHERE post_type LIKE "' . $name . 's")';
        }, $siteIds);
    }

}