<?php
/*
 * Copyright 2026 Alessandro Chiapparini <sprint_planer_web@chiapparini.org>
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * See the LICENSE file in the project root for the full license text.
 */

declare(strict_types=1);

namespace App\Tests\Controllers;

use App\Controllers\UserController;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

/**
 * The demotion guardrails are extracted into a pure static method; this
 * test pins the rule matrix without any SessionGuard / PDO setup.
 */
final class UserControllerTest extends TestCase
{
    /** @return list<array{int,int,bool,bool,int,?string,string}> */
    public static function guardrailCases(): array
    {
        // [actorId, targetId, wasAdmin, willBeAdmin, totalAdmins, expected, label]
        return [
            [1, 1, true,  false, 2, 'self_demote', 'self demote, 2 admins'],
            [1, 1, true,  false, 1, 'self_demote', 'self demote, only admin — self wins priority'],
            [1, 2, true,  false, 1, 'last_admin',  'demote the only admin (different user)'],
            [1, 2, true,  false, 2, null,          'demote someone else when another admin remains'],
            [1, 2, false, true,  1, null,          'promote — never blocked'],
            [1, 2, true,  true,  1, null,          'no-op — never blocked'],
            [1, 2, false, false, 1, null,          'no-op on non-admin — never blocked'],
            [1, 1, false, true,  1, null,          'actor promoting themselves? odd but not blocked'],
        ];
    }

    #[DataProvider('guardrailCases')]
    public function testDemoteGuardrail(
        int $actor,
        int $target,
        bool $was,
        bool $will,
        int $total,
        ?string $expected,
        string $label,
    ): void {
        $this->assertSame(
            $expected,
            UserController::demoteGuardrail($actor, $target, $was, $will, $total),
            $label,
        );
    }

    /** @return list<array{int,int,bool,bool,bool,int,?string,string}> */
    public static function tombstoneCases(): array
    {
        // [actorId, targetId, isTombstoned, isAdmin, tombstone, totalAdmins, expected, label]
        return [
            // self
            [1, 1, false, false, true,  2, 'self_tombstone', 'tombstoning self, even non-admin'],
            [1, 1, true,  false, false, 2, null,             'restoring self (whoever did the original tombstone is gone) — allowed'],
            // last admin guard kicks in only on tombstone, not restore
            [1, 2, false, true,  true,  1, 'last_admin',     'tombstoning the only remaining admin'],
            [1, 2, false, true,  true,  2, null,             'tombstoning an admin when another remains'],
            [1, 2, false, false, true,  1, null,             'tombstoning a non-admin when target is the actor pool — allowed'],
            // restore never trips last_admin (it merely clears the marker; admin status was already 0)
            [1, 2, true,  false, false, 1, null,             'restore is always allowed even with one admin'],
            // noop short-circuits before any other rule
            [1, 1, true,  false, true,  1, 'noop',           'tombstone of already-tombstoned (and self) → noop wins'],
            [1, 2, false, false, false, 1, 'noop',           'restore of already-active → noop'],
        ];
    }

    #[DataProvider('tombstoneCases')]
    public function testTombstoneGuardrail(
        int $actor,
        int $target,
        bool $isTombstoned,
        bool $isAdmin,
        bool $tombstone,
        int $totalAdmins,
        ?string $expected,
        string $label,
    ): void {
        $this->assertSame(
            $expected,
            UserController::tombstoneGuardrail(
                actorId: $actor,
                targetId: $target,
                targetIsTombstoned: $isTombstoned,
                targetIsAdmin: $isAdmin,
                tombstone: $tombstone,
                totalAdmins: $totalAdmins,
            ),
            $label,
        );
    }
}
