<?php

declare(strict_types=1);

namespace Tests\Unit\Framework\ErrorHandling;

use App\Framework\ErrorHandling\ErrorHandlerManager;
use App\Framework\ErrorHandling\ErrorHandlerRegistry;
use App\Framework\ErrorHandling\Handlers\ErrorHandlerInterface;
use App\Framework\ErrorHandling\Handlers\ErrorHandlerPriority;
use App\Framework\ErrorHandling\Handlers\HandlerResult;
use App\Framework\Logging\Logger;

describe('ErrorHandlerManager', function () {
    beforeEach(function () {
        $this->registry = new ErrorHandlerRegistry();
        $this->manager = new ErrorHandlerManager($this->registry);
    });

    it('executes handlers in priority order', function () {
        $executionOrder = [];

        $highPriorityHandler = new class ($executionOrder) implements ErrorHandlerInterface {
            public function __construct(private array &$executionOrder) {}

            public function canHandle(\Throwable $exception): bool
            {
                return true;
            }

            public function handle(\Throwable $exception): HandlerResult
            {
                $this->executionOrder[] = 'high';
                return HandlerResult::create(
                    handled: true,
                    message: 'High priority handler'
                );
            }

            public function getName(): string
            {
                return 'high_priority';
            }

            public function getPriority(): ErrorHandlerPriority
            {
                return ErrorHandlerPriority::HIGH;
            }
        };

        $lowPriorityHandler = new class ($executionOrder) implements ErrorHandlerInterface {
            public function __construct(private array &$executionOrder) {}

            public function canHandle(\Throwable $exception): bool
            {
                return true;
            }

            public function handle(\Throwable $exception): HandlerResult
            {
                $this->executionOrder[] = 'low';
                return HandlerResult::create(
                    handled: true,
                    message: 'Low priority handler'
                );
            }

            public function getName(): string
            {
                return 'low_priority';
            }

            public function getPriority(): ErrorHandlerPriority
            {
                return ErrorHandlerPriority::LOW;
            }
        };

        $this->manager = $this->manager->register($lowPriorityHandler, $highPriorityHandler);

        $exception = new \Exception('Test');
        $this->manager->handle($exception);

        expect($executionOrder)->toBe(['high', 'low']);
    });

    it('stops propagation when handler marks as final', function () {
        $called = [];

        $finalHandler = new class ($called) implements ErrorHandlerInterface {
            public function __construct(private array &$called) {}

            public function canHandle(\Throwable $exception): bool
            {
                return true;
            }

            public function handle(\Throwable $exception): HandlerResult
            {
                $this->called[] = 'final';
                return HandlerResult::create(
                    handled: true,
                    message: 'Final handler',
                    isFinal: true
                );
            }

            public function getName(): string
            {
                return 'final_handler';
            }

            public function getPriority(): ErrorHandlerPriority
            {
                return ErrorHandlerPriority::HIGH;
            }
        };

        $afterHandler = new class ($called) implements ErrorHandlerInterface {
            public function __construct(private array &$called) {}

            public function canHandle(\Throwable $exception): bool
            {
                return true;
            }

            public function handle(\Throwable $exception): HandlerResult
            {
                $this->called[] = 'after';
                return HandlerResult::create(
                    handled: true,
                    message: 'After handler'
                );
            }

            public function getName(): string
            {
                return 'after_handler';
            }

            public function getPriority(): ErrorHandlerPriority
            {
                return ErrorHandlerPriority::LOW;
            }
        };

        $this->manager = $this->manager->register($finalHandler, $afterHandler);

        $exception = new \Exception('Test');
        $result = $this->manager->handle($exception);

        expect($called)->toBe(['final']);
        expect($result->handled)->toBeTrue();
    });

    it('skips handlers that cannot handle exception', function () {
        $specificHandler = new class implements ErrorHandlerInterface {
            public function canHandle(\Throwable $exception): bool
            {
                return $exception instanceof \InvalidArgumentException;
            }

            public function handle(\Throwable $exception): HandlerResult
            {
                return HandlerResult::create(
                    handled: true,
                    message: 'Specific handler'
                );
            }

            public function getName(): string
            {
                return 'specific';
            }

            public function getPriority(): ErrorHandlerPriority
            {
                return ErrorHandlerPriority::HIGH;
            }
        };

        $this->manager = $this->manager->register($specificHandler);

        $exception = new \RuntimeException('Test');
        $result = $this->manager->handle($exception);

        expect($result->handled)->toBeFalse();
        expect($result->results)->toBeEmpty();
    });

    it('continues chain even if handler throws exception', function () {
        $called = [];

        $failingHandler = new class ($called) implements ErrorHandlerInterface {
            public function __construct(private array &$called) {}

            public function canHandle(\Throwable $exception): bool
            {
                return true;
            }

            public function handle(\Throwable $exception): HandlerResult
            {
                $this->called[] = 'failing';
                throw new \RuntimeException('Handler failed');
            }

            public function getName(): string
            {
                return 'failing';
            }

            public function getPriority(): ErrorHandlerPriority
            {
                return ErrorHandlerPriority::HIGH;
            }
        };

        $workingHandler = new class ($called) implements ErrorHandlerInterface {
            public function __construct(private array &$called) {}

            public function canHandle(\Throwable $exception): bool
            {
                return true;
            }

            public function handle(\Throwable $exception): HandlerResult
            {
                $this->called[] = 'working';
                return HandlerResult::create(
                    handled: true,
                    message: 'Working handler'
                );
            }

            public function getName(): string
            {
                return 'working';
            }

            public function getPriority(): ErrorHandlerPriority
            {
                return ErrorHandlerPriority::LOW;
            }
        };

        $this->manager = $this->manager->register($failingHandler, $workingHandler);

        $exception = new \Exception('Test');
        $result = $this->manager->handle($exception);

        expect($called)->toBe(['failing', 'working']);
        expect($result->handled)->toBeTrue();
    });

    it('aggregates results from multiple handlers', function () {
        $handler1 = new class implements ErrorHandlerInterface {
            public function canHandle(\Throwable $exception): bool
            {
                return true;
            }

            public function handle(\Throwable $exception): HandlerResult
            {
                return HandlerResult::create(
                    handled: true,
                    message: 'Handler 1',
                    data: ['from' => 'handler1']
                );
            }

            public function getName(): string
            {
                return 'handler1';
            }

            public function getPriority(): ErrorHandlerPriority
            {
                return ErrorHandlerPriority::HIGH;
            }
        };

        $handler2 = new class implements ErrorHandlerInterface {
            public function canHandle(\Throwable $exception): bool
            {
                return true;
            }

            public function handle(\Throwable $exception): HandlerResult
            {
                return HandlerResult::create(
                    handled: true,
                    message: 'Handler 2',
                    data: ['from' => 'handler2']
                );
            }

            public function getName(): string
            {
                return 'handler2';
            }

            public function getPriority(): ErrorHandlerPriority
            {
                return ErrorHandlerPriority::LOW;
            }
        };

        $this->manager = $this->manager->register($handler1, $handler2);

        $exception = new \Exception('Test');
        $result = $this->manager->handle($exception);

        expect($result->results)->toHaveCount(2);
        expect($result->getMessages())->toBe(['Handler 1', 'Handler 2']);

        $combinedData = $result->getCombinedData();
        expect($combinedData)->toHaveKey('from');
    });
});
