<?php
/**
 * Main Application Class
 *
 * @package Licenzo\Framework\Foundation
 */

namespace Licenzo\Framework\Foundation;

if (!defined('ABSPATH')) {
    exit;
}

/**
 * Application container and lifecycle manager
 */
class Application extends Container
{

    /**
     * Service providers
     *
     * @var array
     */
    protected $serviceProviders = [];

    protected $bootedProviders = [];

    protected $terminatingCallbacks = [];

    /**
     * Registered terminate handlers
     *
     * @var array
     */
    protected array $terminateHandlers = [];

    /**
     * Application version
     *
     * @var string
     */
    public $version = LICENZO_VERSION;

    /**
     * Indicates if the application has been bootstrapped before.
     *
     * @var bool
     */
    protected $hasBeenBootstrapped = false;

    /**
     * Application path
     *
     * @var string
     */
    public $basePath;

    /**
     * Indicates if the application has been booted
     *
     * @var bool
     */
    protected $booted = false;

    /**
     * Stack of currently resolving abstractions to detect circular dependencies
     * @var array
     */
    protected $resolvingStack = [];

    /**
     * Application start time for processing duration tracking
     */
    protected $startTime;

    /**
     * Constructor
     */
    public function __construct($basePath = null) {
        $this->startTime = microtime(true);
        $this->basePath = $basePath ?: LICENZO_PLUGIN_DIR;
        $this->registerBaseBindings();
    }

    /**
     * Get application start time
     *
     * @return float
     */
    public function getStartTime(): float
    {
        return $this->startTime;
    }

    /**
     * Handle incoming request
     *
     * Main entry point for all requests
     *
     * @param mixed $request
     * @return mixed
     * @throws \Throwable
     */
    public function handle($request = null)
    {
        return $this->make('dispatcher')->dispatch($request);
    }

    /**
     * Create a new application builder instance
     *
     * @param array $config
     * @return ApplicationBuilder
     */
    public static function configure($config = [])
    {
        return new ApplicationBuilder($config);
    }

    /**
     * Create a new application instance
     *
     * @param string $basePath
     * @return static
     */
    public static function create($basePath = null) {
        return new static($basePath);
    }

    /**
     * Get configuration value using dot notation
     *
     * @param string $key
     * @param mixed $default
     * @return mixed
     * @throws \Exception
     */
    public function config($key = null, $default = null) {
        $config = $this->make('config');
        return $config->get($key, $default);
    }


    /**
     * Register a service provider with the application
     *
     * @param ServiceProvider|string $provider
     * @param bool $force
     * @return ServiceProvider
     */
    public function register($provider, $force = false)
    {
        if(isset($this->serviceProviders[$provider]) && !$force) {
            return $this->serviceProviders[$provider];
        }

        $provider = $this->resolve($provider);
        $provider->register();
        $this->serviceProviders[] = $provider;
        return $provider;
    }

    /**
     * Register all of the configured providers.
     *
     * @return void
     */
    public function registerConfiguredProviders()
    {
        // For now, we'll register providers that are already configured
        // This method can be extended to load providers from configuration
    }



    /**
     * Boot the application's service providers
     *
     * @return void
     */
    public function boot()
    {
        if ($this->isBooted()) {
            return;
        }

        // Boot all registered service providers
        array_walk($this->serviceProviders, function ($provider) {
            $this->bootProvider($provider);
        });

        $this->booted = true;
    }

    /**
     * Boot the given service provider
     *
     * @param ServiceProvider $provider
     * @return void
     */
    protected function bootProvider(ServiceProvider $provider)
    {
        $provider->boot();
        $this->bootedProviders[] = $provider;
    }

    /**
     * Determine if the application has booted
     *
     * @return bool
     */
    public function isBooted()
    {
        return $this->booted ?? false;
    }

    /**
     * Determine if the application has been bootstrapped before.
     *
     * @return bool
     */
    public function hasBeenBootstrapped()
    {
        return $this->hasBeenBootstrapped;
    }

    /**
     * Run the given array of bootstrap classes.
     *
     * @param array $bootstrappers
     * @return void
     * @throws \Exception
     */
    public function bootstrapWith($bootstrappers)
    {
        foreach ($bootstrappers as $bootstrapper) {
            $this->make($bootstrapper);
        }

        $this->hasBeenBootstrapped = true;
    }



    /**
     * Get the application version
     *
     * @return string
     */
    public function version() {
        return $this->version;
    }

    /**
     * Get base path
     *
     * @param string $path
     * @return string
     */
    public function basePath($path = '') {
        return $this->basePath . ($path ? DIRECTORY_SEPARATOR . $path : '');
    }

    /**
     * Get resource path
     *
     * @param string $path
     * @return string
     */
    public function resourcePath($path = '') {
        return $this->basePath('resources' . ($path ? DIRECTORY_SEPARATOR . $path : ''));
    }

    /**
     * Get asset path
     *
     * @param string $path
     * @return string
     */
    public function assetPath($path = '') {
        return $this->basePath('assets' . ($path ? DIRECTORY_SEPARATOR . $path : ''));
    }

    /**
     * Get asset URL
     *
     * @param string $path
     * @return string
     */
    public function assetUrl($path = '') {
        return LICENZO_PLUGIN_URL . 'assets/' . ltrim($path, '/');
    }



    /**
     * Register base bindings (called by ApplicationBuilder)
     */
    public function registerBaseBindings() {
        // Bind the application instance
        $this->instance('app', $this);
        $this->instance(Application::class, $this);
        $this->instance(Container::class, $this);
        $this->alias(Application::class, 'app');
        $this->alias(Container::class, 'container');
        $this->alias(\Licenzo\Framework\Exceptions\Handler::class, 'exception.handler');
        $this->alias(\Licenzo\Framework\Http\Request::class, 'request');
    }
    /**
     * Register terminate handler(s)
     *
     * @param string|array $handler Class name or array of class names
     * @return $this
     */
    public function terminator($handler)
    {
        if (is_array($handler)) {
            $this->terminateHandlers = array_merge($this->terminateHandlers, $handler);
        } else {
            $this->terminateHandlers[] = $handler;
        }

        return $this;
    }

    /**
     * Handle application shutdown
     *
     * Called from application layer (boot/app.php) on WordPress shutdown hook.
     * Executes termination lifecycle for middleware, handlers, and service providers.
     *
     * @return void
     */
    public function terminate() {
        // 1. Terminate middleware instances (if we have request context)
        if ($this->bound('request.context')) {
            $context = $this->make('request.context');

            if ($context->hasContext()) {
                $request = $context->getRequest();
                $response = $context->getResponse();

                // Call terminate on each tracked middleware instance
                foreach ($context->getMiddleware() as $middleware) {
                    if (method_exists($middleware, 'terminate')) {
                        try {
                            $middleware->terminate($request, $response);
                        } catch (\Throwable $e) {
                            // Silent fail - could add error logging callback later
                        }
                    }
                }

                // Call registered terminate handlers
                foreach ($this->terminateHandlers as $handlerClass) {
                    try {
                        $handler = $this->make($handlerClass);
                        if (method_exists($handler, 'terminate')) {
                            $handler->terminate($request, $response);
                        }
                    } catch (\Throwable $e) {
                        // Silent fail - could add error logging callback later
                    }
                }

                // Clear context for next request
                $context->clear();
            }
        }

        // 2. Terminate all service providers (original functionality)
        foreach ($this->serviceProviders as $provider) {
            $provider->terminate();
        }

        // 3. Clear container instances (original functionality)
        $this->flush();
    }

    /**
     * @throws \Exception
     */
    public function bootstrap()
    {
        $this->boot();
    }
}
