<?php

declare(strict_types=1);

namespace Apexl\Tests\User\UnitTests;

use apexl\Config\ConfigurationMock;
use apexl\Io\modules\display\Controllers\DisplayController;
use apexl\Io\modules\user\services\AccessDeniedRedirect;
use apexl\Io\modules\userDisplay\controllers\LoginDisplayController;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

/**
 * The access-denied redirect opt-in. Pure — deliberately a plain TestCase
 * (no container, no DB).
 */
final class AccessDeniedRedirectTest extends TestCase
{
    // ── the default: an app that sets nothing is untouched ──────────────────

    #[Test]
    public function unset_config_key_reads_as_no_opt_in(): void
    {
        self::assertNull(AccessDeniedRedirect::configured(new ConfigurationMock()));
    }

    #[Test]
    public function null_or_blank_config_reads_as_no_opt_in(): void
    {
        self::assertNull(AccessDeniedRedirect::configured(
            new ConfigurationMock([AccessDeniedRedirect::CONFIG_KEY => null]),
        ));
        self::assertNull(AccessDeniedRedirect::configured(
            new ConfigurationMock([AccessDeniedRedirect::CONFIG_KEY => '   ']),
        ));
    }

    /** With nothing configured, every denied display route still goes to login. */
    #[Test]
    public function without_config_both_anonymous_and_signed_in_go_to_login(): void
    {
        self::assertSame('/login', AccessDeniedRedirect::target(null, false, '/login'));
        self::assertSame('/login', AccessDeniedRedirect::target(null, true, '/login'));
    }

    // ── opted in ────────────────────────────────────────────────────────────

    #[Test]
    public function configured_value_is_returned_trimmed(): void
    {
        self::assertSame('/access-denied', AccessDeniedRedirect::configured(
            new ConfigurationMock([AccessDeniedRedirect::CONFIG_KEY => ' /access-denied ']),
        ));
    }

    #[Test]
    public function only_signed_in_users_are_sent_to_the_access_denied_page(): void
    {
        self::assertSame('/access-denied', AccessDeniedRedirect::target('/access-denied', true, '/login'));
        self::assertSame('/login', AccessDeniedRedirect::target('/access-denied', false, '/login'));
    }

    #[Test]
    public function a_leading_slash_marks_a_literal_path_not_a_route_name(): void
    {
        self::assertTrue(AccessDeniedRedirect::isPath('/access-denied'));
        self::assertFalse(AccessDeniedRedirect::isPath('shell.access-denied'));
    }

    // ── display-route detection ─────────────────────────────────────────────

    #[Test]
    public function detects_a_bare_display_controller_class_string(): void
    {
        self::assertTrue(AccessDeniedRedirect::isDisplayRoute(LoginDisplayController::class));
    }

    /** The case is_subclass_of() on the whole string never matches. */
    #[Test]
    public function detects_a_class_method_callable_string(): void
    {
        self::assertTrue(AccessDeniedRedirect::isDisplayRoute(LoginDisplayController::class . '::index'));
    }

    #[Test]
    public function detects_an_array_callable(): void
    {
        self::assertTrue(AccessDeniedRedirect::isDisplayRoute([LoginDisplayController::class, 'index']));
    }

    #[Test]
    public function detects_the_base_display_controller_itself(): void
    {
        self::assertTrue(AccessDeniedRedirect::isDisplayRoute(DisplayController::class));
    }

    #[Test]
    public function is_false_for_non_display_and_unknown_callables(): void
    {
        self::assertFalse(AccessDeniedRedirect::isDisplayRoute(self::class . '::nope'));
        self::assertFalse(AccessDeniedRedirect::isDisplayRoute('Totally\\Missing\\Klass::index'));
        self::assertFalse(AccessDeniedRedirect::isDisplayRoute(static fn() => null));
        self::assertFalse(AccessDeniedRedirect::isDisplayRoute([]));
    }
}
