<?php
/**
 * Exception Configuration
 *
 * @package Licenzo\Framework\Foundation\Configuration
 */

namespace Licenzo\Framework\Foundation\Configuration;

use Licenzo\Framework\Foundation\Application;
use Licenzo\Framework\Exceptions\Handler;
use Closure;
use Throwable;

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Exception configuration class for fluent exception handling setup
 */
class Exceptions
{
    /**
     * Application instance
     *
     * @var Application
     */
    protected $app;

    /**
     * Custom exception handlers
     *
     * @var array
     */
    protected $handlers = [];

    /**
     * Exception types that should not be reported
     *
     * @var array
     */
    protected $dontReport = [];

    /**
     * Custom reportable callbacks
     *
     * @var array
     */
    protected $reportableCallbacks = [];

    /**
     * Custom renderable callbacks
     *
     * @var array
     */
    protected $renderableCallbacks = [];

    /**
     * Global exception handler enabled
     *
     * @var bool
     */
    protected $globalHandlerEnabled = false;

    /**
     * Constructor
     *
     * @param Application $app
     */
    public function __construct(Application $app)
    {
        $this->app = $app;
    }

    /**
     * Register a custom exception handler
     *
     * @param string $handlerClass
     * @return $this
     */
    public function register(string $handlerClass): self
    {
        $this->handlers[] = $handlerClass;
        return $this;
    }

    /**
     * Specify exception types that should not be reported
     *
     * @param array $exceptions
     * @return $this
     */
    public function dontReport(array $exceptions): self
    {
        $this->dontReport = array_merge($this->dontReport, $exceptions);
        return $this;
    }

    /**
     * Register a reportable callback
     *
     * @param Closure $callback
     * @return $this
     */
    public function reportable(Closure $callback): self
    {
        $this->reportableCallbacks[] = $callback;
        return $this;
    }

    /**
     * Register a renderable callback
     *
     * @param Closure $callback
     * @return $this
     */
    public function renderable(Closure $callback): self
    {
        $this->renderableCallbacks[] = $callback;
        return $this;
    }

    /**
     * Enable global exception handler
     *
     * @param bool $enabled
     * @return $this
     */
    public function global(bool $enabled = true): self
    {
        $this->globalHandlerEnabled = $enabled;
        return $this;
    }

    /**
     * Register exception handler for specific exception type
     *
     * @param string $exceptionType
     * @param Closure $handler
     * @return $this
     */
    public function type(string $exceptionType, Closure $handler): self
    {
        $this->renderableCallbacks[] = function (Throwable $e) use ($exceptionType, $handler) {
            if ($e instanceof $exceptionType) {
                return $handler($e);
            }
            return null;
        };
        return $this;
    }

    /**
     * Set custom error levels to handle
     *
     * @param int $errorLevels
     * @return $this
     */
    public function errorLevels(int $errorLevels): self
    {
        $this->app->config()->set('exceptions.error_levels', $errorLevels);
        return $this;
    }

    /**
     * Enable/disable debug mode for exceptions
     *
     * @param bool $debug
     * @return $this
     */
    public function debug(bool $debug = true): self
    {
        $this->app->config()->set('exceptions.debug', $debug);
        return $this;
    }

    /**
     * Set external reporting services
     *
     * @param array $services
     * @return $this
     */
    public function reportTo(array $services): self
    {
        $this->app->config()->set('exceptions.report_to', $services);
        return $this;
    }

    /**
     * Configure exception logging
     *
     * @param array $config
     * @return $this
     */
    public function logging(array $config): self
    {
        $this->app->config()->set('exceptions.logging', $config);
        return $this;
    }

    /**
     * Apply the exception configuration
     *
     * @return void
     */
    public function apply(): void
    {
        // Get or create the exception handler
        $handler = $this->app->make(Handler::class);

        // Apply don't report configuration
        if (!empty($this->dontReport)) {
            $this->configureDontReport($handler);
        }

        // Apply reportable callbacks
        foreach ($this->reportableCallbacks as $callback) {
            $this->configureReportable($handler, $callback);
        }

        // Apply renderable callbacks
        foreach ($this->renderableCallbacks as $callback) {
            $this->configureRenderable($handler, $callback);
        }

        // Register custom handlers
        foreach ($this->handlers as $handlerClass) {
            $this->app->singleton($handlerClass);
        }

        // Enable global handler if requested
        if ($this->globalHandlerEnabled) {
            $this->app->config()->set('app.enable_global_exception_handler', true);
        }
    }

    /**
     * Configure don't report exceptions
     *
     * @param Handler $handler
     * @return void
     */
    protected function configureDontReport(Handler $handler): void
    {
        // Store in application config for handler to use
        $existing = $this->app->config()->get('exceptions.dont_report', []);
        $this->app->config()->set('exceptions.dont_report', array_merge($existing, $this->dontReport));
    }

    /**
     * Configure reportable callback
     *
     * @param Handler $handler
     * @param Closure $callback
     * @return void
     */
    protected function configureReportable(Handler $handler, Closure $callback): void
    {
        // Store callbacks in application config
        $existing = $this->app->config()->get('exceptions.reportable_callbacks', []);
        $existing[] = $callback;
        $this->app->config()->set('exceptions.reportable_callbacks', $existing);
    }

    /**
     * Configure renderable callback
     *
     * @param Handler $handler
     * @param Closure $callback
     * @return void
     */
    protected function configureRenderable(Handler $handler, Closure $callback): void
    {
        // Store callbacks in application config
        $existing = $this->app->config()->get('exceptions.renderable_callbacks', []);
        $existing[] = $callback;
        $this->app->config()->set('exceptions.renderable_callbacks', $existing);
    }

    /**
     * Get configured handlers
     *
     * @return array
     */
    public function getHandlers(): array
    {
        return $this->handlers;
    }

    /**
     * Get don't report configuration
     *
     * @return array
     */
    public function getDontReport(): array
    {
        return $this->dontReport;
    }

    /**
     * Check if global handler is enabled
     *
     * @return bool
     */
    public function isGlobalHandlerEnabled(): bool
    {
        return $this->globalHandlerEnabled;
    }
}
