<?php

namespace apexl\Io\modules\user\classes;

use apexl\Io\enums\HttpMethod;
use apexl\Io\includes\Entity;
use apexl\Io\modules\display\controllers\displayController;
use apexl\Io\recommendedIncludes\displayModule;
use app\module\newtonsTrailers\Enum\Permission\Common;
use app\module\newtonsTrailers\Interface\PermissionInterface;

class userManagedDisplayModule extends displayModule
{
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Method to add a route, allows the module to have a base path configured and for easy overrides.
     * This Method adds one extra option over the base, to permissions protect a route.
     * @param $verb
     * @param $name
     * @param $callable
     * @param $permission
     * @return userManagedDisplayModule
     */
    protected function addProtectedDisplayRoute(
        HttpMethod $verb,
        string $name,
        string $pattern,
        $callable,
        PermissionInterface $permission = Common::ALLOW_ALL,
    ): static {
        $this->addRoute($verb, $name, 'display/'.ltrim($pattern, '/'), $callable);
        $this->route::addRouteArg($name, 'permission', serialize($permission));

        return $this;
    }

    /**
     * Method to add a route, allows the module to have a base path configured and for easy overrides.
     * This Method adds one extra option over the base, to permissions protect a route.
     * @param $verb
     * @param $name
     * @param $callable
     * @param $permission
     * @return userManagedDisplayModule
     */
    protected function addProtectedDataRoute(
        HttpMethod $verb,
        string $name,
        string $pattern,
        $callable,
        PermissionInterface $permission = Common::ALLOW_ALL,
    ): static {
        $this->addRoute($verb, $name, 'display/data/'.ltrim($pattern, '/'), $callable);
        $this->route::addRouteArg($name, 'permission', serialize($permission));

        return $this;
    }

    /**
     * Method to add a route, allows the module to have a base path configured and for easy overrides.
     * This Method adds one extra option over the base, to permissions protect a route.
     * @param $name
     * @param $callable
     * @param $permission
     * @return userManagedDisplayModule
     */
    protected function addProtectedFormRoute(
        string $name,
        string $pattern,
        $callable,
        PermissionInterface $permission = Common::ALLOW_ALL,
    ): static {
        $this->addDisplayRoute(HttpMethod::GET, $name, 'forms/'.$pattern, $callable);
        $this->route::addRouteArg($name, 'permission', serialize($permission));

        return $this;
    }

    /**
     * Method to add a route, allows the module to have a base path configured and for easy overrides.
     * This Method adds one extra option over the base, to permissions protect a route.
     */
    protected function addProtectedEntityFormRoute(
        Entity $entity,
        string $pattern,
        $callable,
        PermissionInterface $permission = Common::ALLOW_ALL,
    ): static {
        $this->addDisplayRoute(
            HttpMethod::GET,
            $entity->getEntityName().'.entity.form.createUpdate',
            'forms/entities/'.$this->getName().'/'.$pattern,
            $callable
        );
        $this->route::addRouteArg(
            $entity->getEntityName().'.entity.form.createUpdate',
            'permission',
            serialize($permission)
        );
        $this->route::addRouteArg($entity->getEntityName().'.entity.form.createUpdate', 'entity', $entity::class);

        return $this;
    }

    /**
     * Method to add a route, allows the module to have a base path configured and for easy overrides.
     * This Method adds one extra option over the base, to permissions protect a route.
     */
    protected function addProtectedRootRoute(
        HttpMethod $verb,
        string $name,
        string $pattern,
        $callable,
        PermissionInterface $permission = Common::ALLOW_ALL,
    ): static {
        $this->rootRoute($verb, $name, $pattern, $callable);
        $this->route::addRouteArg($name, 'permission', serialize($permission));

        return $this;
    }

    protected function addProtectedAdminRootRoute(
        $verb,
        $name,
        string $pattern,
        $callable,
        PermissionInterface $permission = Common::ALLOW_ALL,
    ): static {
        $this->rootRoute($verb, $name, 'admin/'.$pattern, $callable);
        $this->route::addRouteArg($name, 'permission', serialize($permission));

        return $this;
    }

    /**
     * Declare intention to build routes for this entity. Will take defaults if not previously defined.
     * @param  null  $callables
     * @param  null  $patterns
     * @return $this
     */
    protected function addProtectedEntityRoutes(Entity $entity, $callables = null, $patterns = []): static
    {
        $this->setEntityPatterns($entity, $patterns);

        foreach ($this->entityPatterns[$entity->getEntityName()] as $verb => $pattern) {
            if (isset($callables[$verb]) && is_array($callables[$verb])) {
                $this->setEntityCallable($entity, $verb, ($callables[$verb][0] ?? displayController::class.':'.$verb));
            }
        }

        //build the routes.
        $this->buildEntityRoutes($entity, true);
        //protect the built routes
        foreach ($this->entityPatterns[$entity->getEntityName()] as $verb => $pattern) {
            $this->route::addRouteArg(
                $entity->getEntityName().'.display.'.strtolower((string) $verb),
                'permission',
                serialize($callables[$verb][1] ?? Common::ALLOW_ALL)
            );
            $this->route::addRouteArg(
                $entity->getEntityName().'.display.'.strtolower((string) $verb),
                'entity',
                $entity::class
            );
        }

        return $this;
    }

    /**
     * @param $entity
     * @param  array  $patterns
     * @return $this|userManagedDisplayModule
     */
    protected function setEntityPatterns($entity, array $patterns = []): static
    {
        $this->defaultEntityPatterns = [
            'all' => '/list', //Root display pattern, by default load all entities
            'post' => '/create', //no pattern here as we should POST to the base entity path (by default)
            'put' => '/{'.$entity->primaryKey.'}/edit',
            'delete' => '/{'.$entity->primaryKey.'}/delete',
            'get' => '[/{'.$entity->primaryKey.'}]',
        ];
        $this->patternsOverridden = $patterns !== [];
        $this->entityPatterns[$entity->getEntityName()] = array_merge($this->defaultEntityPatterns, $patterns);

        return $this;
    }

    /**
     * Normal structure is /ModuleName/EntityName/{pattern}
     * Build any default entity routes.
     * @param $entity
     * @param  bool  $rootRoutes
     */
    protected function buildEntityRoutes($entity, bool $rootRoutes = false): void
    {
        $method = $rootRoutes ? 'rootRoute' : 'addRoute';
        foreach ($this->entityPatterns[$entity->getEntityName()] as $verb => $pattern) {
            if (isset($this->entityControllers[$entity->getEntityName()][$verb])) {
                $basePattern = $this->patternsOverridden ? '' : (str_replace(
                    'entity',
                    '',
                    strtolower((string) $entity->getEntityName())
                ));
                $this->$method(
                    HttpMethod::GET, //Always Get for display.
                    $entity->getEntityName().'.display.'.strtolower((string) $verb),
                    $basePattern.$pattern,
                    $this->entityControllers[$entity->getEntityName()][$verb]
                );
            }
        }
    }
}
