<?php
/**
 * @package SP Page Builder
 * @author JoomShaper http://www.joomshaper.com
 * @copyright Copyright (c) 2010 - 2024 JoomShaper
 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPLv2 or later
 */

namespace JoomShaper\SPPageBuilder\DynamicContent;

defined('_JEXEC') or die;

use Joomla\CMS\Session\Session;
use Joomla\Input\Input;
use JoomShaper\SPPageBuilder\DynamicContent\Http\Request;
use JoomShaper\SPPageBuilder\DynamicContent\Http\Response;
use JoomShaper\SPPageBuilder\DynamicContent\Supports\Arr;

class DynamicContent
{
    /**
     * The instance of the dynamic content
     * 
     * @var self
     * @since 5.5.0
     */
    private static $instance = null;

    /**
     * The controller instance
     * 
     * @var Controller
     * @since 5.5.0
     */
    private $controller = null;

    /**
     * The controller namespace
     * 
     * @var string
     * @since 5.5.0
     */
    protected const CONTROLLER_NAMESPACE = "JoomShaper\\SPPageBuilder\\DynamicContent\\Controllers\\";

    /**
     * The service namespace
     * 
     * @var string
     * @since 5.5.0
     */
    protected const SERVICE_NAMESPACE = "JoomShaper\\SPPageBuilder\\DynamicContent\\Services\\";

    /**
     * Constructor method
     * 
     * @param Controller $controller The controller
     * 
     * @since 5.5.0
     */
    private function __construct($controller)
    {
        $this->controller = $controller;
    }

    /**
     * Create a new instance of the dynamic content
     * 
     * @param Controller $controller The controller
     * 
     * @return self
     * @since 5.5.0
     */
    public static function create($controller)
    {
        if (self::$instance === null) {
            self::$instance = new static($controller);
        }

        return self::$instance;
    }

    /**
     * Dispatch the request
     * 
     * @param string $task The task
     * @param Input $input The input
     * 
     * @return Response
     * @since 5.5.0
     */
    public function dispatch(string $task, Input $input)
    {
        /** @var Request $request */
        $request = new Request($input->getArray());
        $context = $request->getString('_context', '');

        if (empty($context)) {
            return response()->json(['message' => 'Context is required. Please provide a valid context for the request using the `_context` parameter.'], Response::HTTP_BAD_REQUEST);
        }

        if (empty($task)) {
            return response()->json(['message' => 'Task is required'], Response::HTTP_BAD_REQUEST);
        }

        if (!Session::checkToken()) {
            return response()->json(['message' => 'Invalid token provided'], Response::HTTP_UNAUTHORIZED);
        }

        $task = $this->createRequestMethod($task);
        $controllerClass = $this->createControllerClassName($context);

        if (!class_exists($controllerClass)) {
            return response()->json(['message' => sprintf('Controller class `%s` not found', $controllerClass)], Response::HTTP_NOT_FOUND);
        }

        $serviceClass = $this->createServiceClassName($context);
        $service = null;

        if (class_exists($serviceClass)) {
            $service = new $serviceClass();
        }

        $controller = new $controllerClass($service, $this->controller);

        if (!method_exists($controller, $task)) {
            return response()->json(['message' => 'Invalid endpoint provided'], Response::HTTP_NOT_FOUND);
        }

        return $controller->$task($request);
    }

    /**
     * Create a request method from the request parameter named task.
     * 
     * @param string $task The task
     * 
     * @return string
     * @since 5.5.0
     */
    protected function createRequestMethod(string $task)
    {
        $task = preg_replace('/[^a-zA-Z0-9-]/', '', $task);
        $parts = explode('-', $task);
        return Arr::make($parts)->map(function ($part, $index) {
            $part = strtolower($part);
            if ($index === 0) {
                return $part;
            }
            return ucfirst($part);
        })->join('');
    }

    /**
     * Create a controller class name from the context
     * 
     * @param string $context The context
     * 
     * @return string
     * @since 5.5.0
     */
    protected function createControllerClassName(string $context)
    {
        $context = preg_replace('/[^a-zA-Z0-9-]/', '', $context);
        $parts = explode('-', $context);
        $baseName = Arr::make($parts)->map(function ($part) {
            $part = strtolower($part);
            return ucfirst($part);
        })->join('');

        return static::CONTROLLER_NAMESPACE . $baseName . 'Controller';
    }

    /**
     * Create a service class name from the context
     * 
     * @param string $context The context
     * 
     * @return string
     * @since 5.5.0
     */
    protected function createServiceClassName(string $context)
    {
        $context = preg_replace('/[^a-zA-Z0-9-]/', '', $context);
        $parts = explode('-', $context);
        $baseName = Arr::make($parts)->map(function ($part) {
            $part = strtolower($part);
            return ucfirst($part);
        })->join('');

        return static::SERVICE_NAMESPACE . $baseName . 'Service';
    }
}
