<?php

namespace Velis\Model\DataObject;

use Exception;
use Phalcon\Cli\Router as CliRouter;
use ReflectionClass;
use Velis\App;
use Velis\Exception as VelisException;
use Velis\Model\Routable;
use Velis\Mvc\Router;

trait UrlTrait
{
    abstract protected function _getPrimaryKeyParam();

    /**
     * Generates url if Routable interface instance
     * @throws Exception
     */
    public function url(?string $route = null): ?string
    {
        $hasRoutes = !App::isConsole() && !App::isApi();

        if ($route && $hasRoutes) {
            return App::getRouteUrl($route, $this->_getPrimaryKeyParam());
        } elseif (!$route && $this instanceof Routable) {
            if ($this->hasFrontendRouting()) {
                return $this->getFrontendRoute();
            }

            if (!$hasRoutes) {
                // Phalcon router throws an exception if route is not found
                try {
                    $route = App::getRouteUrl($this->getRouteName(), $this->_getPrimaryKeyParam());

                    if ($route) {
                        return $route;
                    }
                } catch (Exception $e) {
                    // ignore
                }

                return $this->getStandardUrl();
            } else {
                return App::getRouteUrl($this->getRouteName(), $this->_getPrimaryKeyParam());
            }
        }

        return null;
    }

    /**
     * Returns true if the object's module has React frontend routing enabled
     */
    protected function hasFrontendRouting(): bool
    {
        // di returns cli router for console usage
        /** @var Router|CliRouter $router */
        $router = App::getService('router');

        if (!($router instanceof Router)) {
            return false;
        }

        $module = $this->getFrontendModuleName();

        return $module
            && $router->isFrontendModuleSwitched($module)
            && $this->getFrontendRoute();
    }

    /**
     * Returns the name of frontend module with which the object is associated by routing
     * Override in child class if the frontend module does not correspond to the object's module namespace
     */
    protected function getFrontendModuleName(): ?string
    {
        $reflection = new ReflectionClass($this);
        $baseModuleName = $reflection->getNamespaceName();

        /** @var Router $router */
        $router = App::getService('router');
        $switchableModules = $router->getSwitchableModules();

        if (isset($switchableModules[$baseModuleName])) {
            return $switchableModules[$baseModuleName];
        }

        return null;
    }


    /**
     * Placeholder for method returning object's URL for React-based frontend. Override it in child classes.
     * @throws Exception
     */
    protected function getFrontendRoute(): string
    {
        VelisException::raise('getFrontendRoute method not implemented for ' . get_class($this));
        return '';
    }
}
