<?php

/**
 * Admin notifications. Dispatchment is delayed.
 *
 * @author     Time.ly Network Inc.
 * @since      2.0
 *
 * @package    AI1EC
 * @subpackage AI1EC.Notification
 */
class Ai1ec_Notification_Admin extends Ai1ec_Notification {

    /**
     * @var string Option key for messages storage.
     */
    const OPTION_KEY   = 'ai1ec_admin';

    /**
     * @var string Name of messages for all admins.
     */
    const RCPT_ALL     = 'all';

    /**
     * @var string Name of network-admin only messages.
     */
    const RCPT_NETWORK = 'network_admin_notices';

    /**
     * @var string Name of admin only messages.
     */
    const RCPT_ADMIN   = 'admin_notices';

    /**
     * @var array Map of messages to be rendered.
     */
    protected $_message_list = array();

    /**
     * Add message to store.
     *
     * @param string $message    Actual message.
     * @param string $class      Message box class.
     * @param int    $importance Optional importance parameter for the message.
     * Levels of importance are as following:
     *     - 0 - messages limited to Ai1EC pages;
     *     - 1 - messages limited to [0] and Plugins/Updates pages;
     *     - 2 - messages limited to [1] and Dashboard.
     * @param array  $recipients List of message recipients.
     * @param bool   $persistent If set to true, messages needs to be dismissed by user.
     *
     * @return bool Success.
     */
    public function store(
        $message,
        $class            = 'updated',
        $importance       = 0,
        array $recipients = array( self::RCPT_ADMIN ),
        $persistent = false
    ) {
        $this->retrieve();

        $entity  = compact( 'message', 'class', 'importance', 'persistent' );
        $msg_key = sha1( json_encode( $entity ) );
        $entity['msg_key'] = $msg_key;
        if ( isset( $this->_message_list['_messages'][$msg_key] ) ) {
            return true;
        }
        $this->_message_list['_messages'][$msg_key] = $entity;
        foreach ( $recipients as $rcpt ) {
            if ( ! isset( $this->_message_list[$rcpt] ) ) {
                continue;
            }
            $this->_message_list[$rcpt][$msg_key] = $msg_key;
        }
        return $this->write();
    }

    /**
     * Replace database representation with in-memory list version.
     *
     * @return bool Success.
     */
    public function write() {
        return $this->_registry->get( 'model.option' )
            ->set( self::OPTION_KEY, $this->_message_list );
    }

    /**
     * Update in-memory list from data store.
     *
     * @return Ai1ec_Notification_Admin Instance of self for chaining.
     */
    public function retrieve() {
        static $default = array(
            '_messages'        => array(),
            self::RCPT_ALL     => array(),
            self::RCPT_NETWORK => array(),
            self::RCPT_ADMIN   => array(),
        );
        $this->_message_list = $this->_registry->get( 'model.option' )
            ->get( self::OPTION_KEY, null );
        if ( null === $this->_message_list ) {
            $this->_message_list = $default;
        } else {
            $this->_message_list = array_merge(
                $default,
                $this->_message_list
            );
        }
        return $this;
    }

    /**
     * Display messages.
     *
     * @wp_hook network_admin_notices
     * @wp_hook admin_notices
     *
     * @return bool Update status.
     */
    public function send() {
        $this->retrieve();

        $destinations = array( self::RCPT_ALL, current_filter() );
        $modified     = false;
        foreach ( $destinations as $dst ) {
            if ( ! empty( $this->_message_list[$dst] ) ) {
                foreach ( $this->_message_list[$dst] as $key ) {
                    if (
                        isset( $this->_message_list['_messages'][$key] )
                    ) {
                        $this->_render_message(
                            $this->_message_list['_messages'][$key]
                        );
                        if (
                            ! isset( $this->_message_list['_messages'][$key]['persistent'] ) ||
                            false === $this->_message_list['_messages'][$key]['persistent']
                        ) {
                            unset( $this->_message_list['_messages'][$key] );
                            unset( $this->_message_list[$dst][$key] );
                        }
                    }
                }
                $modified                  = true;
            }
        }
        if ( ! $modified ) {
            return false;
        }
        return $this->write();
    }

    /**
     * Delete a notice from ajax call.
     *
     */
    public function dismiss_notice() {
        $key = $_POST['key'];
        foreach ( $this->_message_list as $dest ) {
            if ( isset( $this->_message_list[$dest][$key] ) ) {
                unset( $this->_message_list[$dest][$key] );
            }
        }
        $this->write();
    }

    protected function _render_message( array $entity ) {
        $importance = 0;
        if ( isset( $entity['importance'] ) ) {
            $importance = ( (int)$entity['importance'] ) % 3;
        }
        if ( $this->are_notices_available( $importance ) ) {
            static $theme = null;
            if ( null === $theme ) {
                $theme = $this->_registry->get( 'theme.loader' );
            }
            $entity['text_label'] = apply_filters(
                'ai1ec_notification_label',
                Ai1ec_I18n::__( 'All-in-One Event Calendar' )
            );
            $entity['text_dismiss_button'] = Ai1ec_I18n::__( 'Got it – dismiss this' );
            $file = $theme->get_file(
                'notification/admin.twig',
                $entity,
                true
            );
            $file->render();
        }
    }

    /**
     * Check whereas our notices should be displayed on this page.
     *
     * Limits notices to Ai1EC pages and WordPress "Plugins", "Updates" pages.
     * Important notices are also displayable in WordPress "Dashboard".
     * Levels of importance (see $importance) are as following:
     *     - 0 - messages limited to Ai1EC pages;
     *     - 1 - messages limited to [0] and Plugins/Updates pages;
     *     - 2 - messages limited to [1] and Dashboard.
     *
     * @param int $importance The level of importance. See above for details.
     *
     * @return bool Availability
     */
    public function are_notices_available( $importance ) {
        // In CRON `get_current_screen()` is not present
        // and we wish to have notice on all "our" pages

        $acl = $this->_registry->get( 'acl.aco' );
        if ( $acl->is_all_events_page() || $acl->are_we_editing_our_post() ) {
            return true;
        }

        if ( $importance < 1 ) {
            return false;
        }

        $screen = null;
        if ( is_callable( 'get_current_screen' ) ) {
            $screen = get_current_screen();
        }

        $allow_on = array(
            'plugins',
            'update-core',
        );
        if ( $importance > 1 ) {
            $allow_on[] = 'dashboard';
        }
        if (
            is_object( $screen ) &&
            isset( $screen->id ) &&
            in_array( $screen->id, $allow_on )
        ) {
            return true;
        }
        return false;
    }

}
