<?php
namespace SabaiApps\Directories\Component\FrontendSubmit;

use SabaiApps\Directories\Component\AbstractComponent;
use SabaiApps\Directories\Component\System;
use SabaiApps\Directories\Component\Field;
use SabaiApps\Directories\Component\Display;
use SabaiApps\Directories\Component\Entity;
use SabaiApps\Directories\Component\CSV;
use SabaiApps\Framework\User\AbstractIdentity;
use SabaiApps\Directories\Context;
use SabaiApps\Directories\Application;
use SabaiApps\Directories\Request;

class FrontendSubmitComponent extends AbstractComponent implements
    System\IMainRouter,
    Field\ITypes,
    Field\IWidgets,
    System\ISlugs,
    Display\IButtons,
    CSV\IExporters,
    CSV\IImporters,
    IRestrictors
{
    const VERSION = '1.2.81', PACKAGE = 'directories-frontend';
    
    public static function description()
    {
        return 'Enables registered and non-registered users to submit content from the frontend.';
    }
    
    public function systemMainRoutes($lang = null)
    {
        $routes = [];
        if ($this->hasSlug('login', $lang)) {
            $login_slug = $this->getSlug('login', $lang);
            $routes['/' . $login_slug] = [
                'controller' => 'LoginOrRegister',
                'access_callback' => true,
                'callback_path' => 'login',
                'title_callback' => true,
            ];
            $routes['/' . $login_slug . '/lost_password'] = [
                'controller' => 'LostPassword',
                'callback_path' => 'lost_password',
                'title_callback' => true,
            ];
            $routes['/' . $login_slug . '/reset_password'] = [
                'controller' => 'ResetPassword',
                'access_callback' => true,
                'callback_path' => 'reset_password',
                'title_callback' => true,
            ];
        }
        foreach ($this->_application->Entity_Bundles() as $bundle) {
            if (!$this->_application->isComponentLoaded($bundle->component)
                || !empty($bundle->info['is_taxonomy'])
            ) continue;

            if (empty($bundle->info['parent'])) {
                $routes['/' . $this->_application->FrontendSubmit_AddEntitySlug($bundle, $lang)] = array(
                    'controller' => 'AddEntity',
                    'access_callback' => true,
                    'title_callback' => true,
                    'callback_path' => 'add_entity',
                    'data' => array(
                        'bundle_type' => $bundle->type,
                    ),
                );
            } else {
                $routes['/' . $this->_application->FrontendSubmit_AddEntitySlug($bundle, $lang)] = array(
                    'controller' => 'AddChildEntity',
                    'access_callback' => true,
                    'title_callback' => true,
                    'callback_path' => 'add_child_entity',
                    'data' => array(
                        'bundle_type' => $bundle->type,
                        'component' => $bundle->component,
                        'group' => $bundle->group,
                    ),
                );
            }
        }
        
        return $routes;
    }
    
    protected function _getBundle(array $route)
    {
        return $this->_application->Entity_Bundle($route['data']['bundle_type'], $route['data']['component'], $route['data']['group']);
    }

    public function systemOnAccessMainRoute(Context $context, $path, $accessType, array &$route)
    {
        switch ($path) {
            case 'add_child_entity':
                if (!isset($context->child_bundle)) {
                    if (!$bundle = $this->_getBundle($route)) return false;

                    $context->child_bundle = $bundle;
                }
            case 'add_entity':
                if ($accessType === Application::ROUTE_ACCESS_LINK) {
                    $context->bundle_type = $route['data']['bundle_type'];
                }
                return true;
            case 'login':
                if ($accessType === Application::ROUTE_ACCESS_LINK
                    || $this->_application->getUser()->isAnonymous()
                ) return true;

                // Redirect to frontend dashboard if enabled
                if ($this->_application->isComponentLoaded('Dashboard')
                    && ($dashboard_slug = $this->_application->getComponent('Dashboard')->getSlug('dashboard'))
                ) {
                    $context->setRedirect('/' . $dashboard_slug);
                }
                return false;
            case 'reset_password':
                if ($accessType === Application::ROUTE_ACCESS_LINK) {
                    if (!$cookie = $this->_application->Cookie('drts-frontendsubmit-reset-password')) {
                        // No cookie, so set a cookie and redirect if valid id/key

                        if ((!$key = $context->getRequest()->asStr('key'))
                            || (!$id = $context->getRequest()->asInt('id'))
                            || (!$identity = $this->_application->getPlatform()->getUserIdentityFetcher()->fetchById($id))
                        ) return false;

                        try {
                            if (!$this->_application->getPlatform()->checkResetPasswordKey($key, $identity)) return false;
                        } catch (\Exception $e) {
                            $this->_application->logError($e);
                            return false;
                        }

                        if (!headers_sent()) {
                            $this->_application->Cookie('drts-frontendsubmit-reset-password', sprintf('%d:%s', $id, $key));
                            $context->setRedirect($route['path']);
                            return false;
                        }
                    }
                    if (!isset($key)
                        || !isset($identity)
                    ) {
                        if ((!$parts = explode(':', $cookie))
                            || count($parts) !== 2
                            || (!$id = intval($parts[0]))
                            || (!$key = $parts[1])
                            || (!$identity = $this->_application->getPlatform()->getUserIdentityFetcher()->fetchById($id))
                        ) return false;
                    }

                    try {
                        if (!$this->_application->getPlatform()->checkResetPasswordKey($key, $identity)) return false;
                    } catch (\Exception $e) {
                        $this->_application->logError($e);
                        return false;
                    }
                    $context->identity = $identity;
                }
                return true;
        }
    }

    public function systemMainRouteTitle(Context $context, $path, $titleType, array $route)
    {
        switch ($path) {
            case 'login':
                return $this->getTitle('login');
            case 'lost_password':
                return __('Lost your password?', 'directories-frontend');
            case 'reset_password':
                return __('Reset Password', 'directories-frontend');
            case 'add_child_entity_non_public':
            case 'add_child_entity':
                return $context->child_bundle->getLabel('add');
            case 'add_entity':
                return $this->_application->FrontendSubmit_AddEntitySlug_title($context->bundle_type);
        }
    }
    
    public function fieldGetTypeNames()
    {
        return ['frontendsubmit_guest'];
    }
    
    public function fieldGetType($name)
    {
        switch ($name) {
            case 'frontendsubmit_guest':
                return new FieldType\GuestFieldType($this->_application, $name);
        }
    }
    
    public function fieldGetWidgetNames()
    {
        return ['frontendsubmit_guest'];
    }
    
    public function fieldGetWidget($name)
    {
        switch ($name) {
            case 'frontendsubmit_guest':
                return new FieldWidget\GuestFieldWidget($this->_application, $name);
        }
    }
    
    public function systemSlugs()
    {
        return array(
            'login' => array(
                'admin_title' => __('Login/Registration', 'directories-frontend'),
                'title' => __('Login or Register', 'directories-frontend'),
                'wp_shortcode' => 'drts-frontend-login',
                'required' => false,
            ),
        );
    }
        
    public function onCoreLoginUrlFilter(&$url, $redirect)
    {
        if ($this->hasSlug('login')) {
            $url = (string)$this->_application->Url('/' . $this->getSlug('login'), array('redirect_to' => $redirect));
        }
    }
    
    public function onSystemSlugsFilter(&$slugs)
    {
        foreach ($this->_application->Entity_Bundles() as $bundle) {
            if (!$this->_application->isComponentLoaded($bundle->component)
                || !empty($bundle->info['is_taxonomy'])
                || !empty($bundle->info['parent'])
                || !empty($bundle->info['internal'])
            ) continue;
            
            $add_slug = $this->_application->FrontendSubmit_AddEntitySlug_name($bundle->type);
            if (isset($slugs[$this->_name][$add_slug])) continue;
            
            $info = $this->_application->Entity_BundleTypeInfo($bundle);
            $slugs[$this->_name][$add_slug] = array(
                'slug' => $add_slug,
                'admin_title' => $info['label_add'],
                'title' => $info['label_add'],
                'component' => $this->_name,
                'wp_shortcode' => 'drts-' . $add_slug . '-form',
             );
        }
    }
    
    public function onEntityIsAuthorFilter(&$false, Entity\Type\IEntity $entity)
    {
        if (!$entity->getAuthorId() // must be a guest post
            && ($guid = $entity->getSingleFieldValue('frontendsubmit_guest', 'guid'))
            && ($cookie_guids = $this->_application->FrontendSubmit_GuestAuthorCookie_guids())
            && in_array($guid, $cookie_guids)
        ) {
            $false = true;
        }
    }
    
    public function onEntityAuthorFilter(AbstractIdentity $author, Entity\Type\IEntity $entity)
    {
        if ($author->isAnonymous()
            && $author->name === null
            && ($guest = $entity->getSingleFieldValue('frontendsubmit_guest'))
        ) {
            foreach (['name', 'email', 'url'] as $key) {
                if (strlen($guest[$key])) $author->$key = $guest[$key];
            }
        }
    }
    
    protected function _onEntityCreateBundlesSuccess(array $bundles, $update = false)
    {
        foreach ($bundles as $bundle) {
            if (empty($bundle->info['is_taxonomy'])
                && $this->_application->Entity_BundleTypeInfo($bundle, 'frontendsubmit_guest')
            ) {
                $this->_application->getComponent('Entity')->createEntityField(
                    $bundle,
                    'frontendsubmit_guest',
                    array(
                        'type' => 'frontendsubmit_guest',
                        'max_num_items' => 1,
                    )
                );
            }
        }
    }
    
    public function onEntityCreateBundlesSuccess(array $bundles)
    {
        $this->_onEntityCreateBundlesSuccess($bundles);
    }
    
    public function onEntityUpdateBundlesSuccess(array $bundles)
    {
        $this->_onEntityCreateBundlesSuccess($bundles, true);
    }
    
    public function onEntityPermissionsFilter(&$permissions, Entity\Model\Bundle $bundle)
    {
        // Enable some guest permissions
        if (empty($bundle->info['is_taxonomy'])) {
            if (!empty($bundle->info['public'])) {
                $permissions['entity_create']['guest_allowed'] = true;
                $permissions['entity_publish']['guest_allowed'] = true;
                //$permissions['entity_edit']['guest_allowed'] = true;
                //$permissions['entity_edit_published']['guest_allowed'] = true;
                //$permissions['entity_delete']['guest_allowed'] = true;
                //$permissions['entity_delete_published']['guest_allowed'] = true;
            }
        } else {
            $permissions['entity_assign']['guest_allowed'] = true;
        }
    }
    
    public function displayGetButtonNames(Entity\Model\Bundle $bundle)
    {
        $ret = [];
        if (empty($bundle->info['is_taxonomy'])) {
            if (empty($bundle->info['parent'])) {
                foreach ($this->_application->Entity_BundleTypes_children($bundle->type) as $bundle_type) {
                    if (!$this->_application->Entity_Bundle($bundle_type, $bundle->component, $bundle->group)) continue;
                    
                    $ret[] = 'frontendsubmit_add_' . $bundle_type;
                }
            }
        }
        return $ret;
    }
    
    public function displayGetButton($name)
    {
        return new DisplayButton\AddEntityDisplayButton($this->_application, $name);
    }
    
    public function onDirectoryAdminSettingsFormFilter(&$form)
    {
        $form['#tabs'][$this->_name] = array(
            '#title' => __('Frontend Submit', 'directories-frontend'),
            '#weight' => 16,
        );
        $form[$this->_name] = array(
            '#tree' => true,
            '#component' => $this->_name,
            '#tab' => $this->_name,
        ) + $this->_application->FrontendSubmit_SettingsForm($this->_config, array($this->_name));
    }

    public function onDirectoryContentTypeSettingsFormFilter(&$form, $directoryType, $contentType, $info, $settings, $parents, $submitValues)
    {
        if (!isset($info['frontendsubmit_enable'])
            || !$info['frontendsubmit_enable']
            || !empty($info['parent'])
            || !empty($info['is_taxonomy'])
        ) return;

        $form['frontendsubmit_enable'] = array(
            '#type' => 'checkbox',
            '#title' => __('Enable frontend submit', 'directories-frontend'),
            '#default_value' => !empty($settings['frontendsubmit_enable'])
                || !isset($settings['frontendsubmit_enable']) // for compat with < 1.1.2
                || is_null($settings),
            '#horizontal' => true,
        );
    }

    public function onDirectoryContentTypeInfoFilter(&$info, $contentType, $settings = null)
    {
        if (!isset($info['frontendsubmit_enable'])) return;

        if (!$info['frontendsubmit_enable']
            || !empty($info['is_taxonomy'])
        ) {
            unset($info['frontendsubmit_enable']);
        }

        if (!empty($info['parent'])) {
            $info['frontendsubmit_enable'] = true;
        } else {
            if (isset($settings['frontendsubmit_enable']) && !$settings['frontendsubmit_enable']) {
                $info['frontendsubmit_enable'] = false;
            }
        }
    }

    public function onEntityBundleInfoKeysFilter(&$keys)
    {
        $keys[] = 'frontendsubmit_enable';
        $keys[] = 'frontendsubmit_restrict';
    }
    
    public function csvGetImporterNames()
    {
        return ['frontendsubmit_guest'];
    }
    
    public function csvGetImporter($name)
    {
        return new CSVImporter\GuestCSVImporter($this->_application, $name);
    }
    
    public function csvGetExporterNames()
    {
        return ['frontendsubmit_guest'];
    }
    
    public function csvGetExporter($name)
    {
        return new CSVExporter\GuestCSVExporter($this->_application, $name);
    }


    public function frontendsubmitGetRestrictorNames()
    {
        return ['default'];
    }

    public function frontendsubmitGetRestrictor($name)
    {
        return new Restrictor\DefaultRestrictor($this->_application, $name);
    }

    public function isLoginFormEnabled()
    {
        if (!$slug = $this->hasSlug('login')) return false;

        if ($this->_application->getPlatform()->isLoginFormRequired()) return $slug;

        return (!empty($this->_config['login']['form'])
            || (!isset($this->_config['login']['form']) && !empty($this->_config['login']['login_form'])) // for compat with <=1.2.42
        ) ? $slug : false;
    }

    public function isRegisterFormEnabled()
    {
        if ($this->_application->getPlatform()->isRegisterFormRequired()) return true;

        return !empty($this->_config['register']['form'])
            || (!isset($this->_config['register']['form']) && !empty($this->_config['login']['register_form'])); // for compat with <=1.2.42
    }

    public function isCollectGuestInfo()
    {
        $config = empty($this->_config['guest']) ? [] : $this->_config['guest'];
        $ret = !empty($config['collect_name'])
            || !isset($config['collect_name']) // compat with <1.2.58
            || !empty($config['collect_email'])
            || !empty($config['collect_url'])
            || !empty($config['collect_privacy']);
        return $this->_application->Filter('frontendsubmit_collect_guest_info', $ret);
    }

    public function onWordPressDoShortcodeFilter(&$ret, $shortcode, $component)
    {
        if (strpos($shortcode, 'drts-frontend-login') !== 0
            || !$this->isLoginFormEnabled()
        ) return;

        $path = '/' . $this->getSlug('login');
        $url = (string)$this->_application->Url($path);
        $url_requested = (string)Request::url(false);
        if (strpos($url_requested, $url) === 0
            && ($_path = substr($url_requested, strlen($url)))
            && ($_path = trim($_path, '/'))
        ) {
            $ret['path'] = [
                'path' => $path . '/' . $_path, // additional path
                'params' => empty($_REQUEST) ? [] : $_REQUEST,
            ];
        }
    }

    public function onEntityIsRoutableFilter(&$isRoutable, $bundle, $action, $entity = null)
    {
        if ($isRoutable === false
            || $action !== 'add'
            || !empty($bundle->info['parent'])
            || !empty($bundle->info['frontendsubmit_enable'])
        ) return;

        $isRoutable = false;
    }
}