<?php

declare(strict_types=1);

namespace OCA\Mitgliederverwaltung\Tests\Integration;

use OCA\Mitgliederverwaltung\Controller\PermissionController;
use OCA\Mitgliederverwaltung\Db\Permission;
use OCA\Mitgliederverwaltung\Service\PermissionService;
use OCA\Mitgliederverwaltung\Service\ValidationException;
use OCP\AppFramework\Http;
use OCP\IRequest;
use OCP\IUser;
use OCP\IUserManager;
use OCP\IUserSession;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;

/**
 * Integration-level tests for PermissionController API endpoints.
 */
class PermissionControllerTest extends TestCase {

    private PermissionController $controller;
    private PermissionService&MockObject $permissionService;
    private IUserManager&MockObject $userManager;
    private IUserSession&MockObject $userSession;
    private IRequest&MockObject $request;
    private LoggerInterface&MockObject $logger;

    protected function setUp(): void {
        parent::setUp();

        $this->permissionService = $this->createMock(PermissionService::class);
        $this->userManager = $this->createMock(IUserManager::class);
        $this->userSession = $this->createMock(IUserSession::class);
        $this->request = $this->createMock(IRequest::class);
        $this->logger = $this->createMock(LoggerInterface::class);

        // Default: authenticated user
        $user = $this->createMock(IUser::class);
        $user->method('getUID')->willReturn('admin');
        $this->userSession->method('getUser')->willReturn($user);

        $this->controller = new PermissionController(
            'mitgliederverwaltung',
            $this->request,
            $this->permissionService,
            $this->userManager,
            $this->userSession,
            $this->logger
        );
    }

    // ── GET /api/v1/permissions ────────────────────────────────────

    public function testIndexReturns200(): void {
        $perm = new Permission();
        $perm->setNcUserId('testuser');
        $perm->setLevel('editor');

        $this->permissionService->method('getAllPermissions')
            ->willReturn([$perm]);

        $response = $this->controller->index();

        $this->assertSame(Http::STATUS_OK, $response->getStatus());
        $data = $response->getData();
        $this->assertCount(1, $data);
        $this->assertSame('testuser', $data[0]['ncUserId']);
    }

    public function testIndexReturns500OnException(): void {
        $this->permissionService->method('getAllPermissions')
            ->willThrowException(new \RuntimeException('fail'));

        $response = $this->controller->index();

        $this->assertSame(Http::STATUS_INTERNAL_SERVER_ERROR, $response->getStatus());
    }

    // ── GET /api/v1/permissions/users ──────────────────────────────

    public function testListUsersReturns200(): void {
        // callForAllUsers uses a callback; we simulate it
        $this->userManager->method('callForAllUsers')
            ->willReturnCallback(function (callable $callback) {
                $user = $this->createMock(IUser::class);
                $user->method('getUID')->willReturn('user1');
                $user->method('getDisplayName')->willReturn('User One');
                $callback($user);
            });

        $response = $this->controller->listUsers();

        $this->assertSame(Http::STATUS_OK, $response->getStatus());
        $data = $response->getData();
        $this->assertCount(1, $data);
        $this->assertSame('user1', $data[0]['uid']);
    }

    public function testListUsersReturns500OnException(): void {
        $this->userManager->method('callForAllUsers')
            ->willThrowException(new \RuntimeException('fail'));

        $response = $this->controller->listUsers();

        $this->assertSame(Http::STATUS_INTERNAL_SERVER_ERROR, $response->getStatus());
    }

    // ── PUT /api/v1/permissions/{ncUserId} ─────────────────────────

    public function testSetPermissionReturns200OnSuccess(): void {
        // Setting permission for a different user
        $perm = new Permission();
        $perm->setNcUserId('otheruser');
        $perm->setLevel('editor');

        $this->permissionService->method('setPermission')
            ->willReturn($perm);

        $response = $this->controller->setPermission('otheruser');

        $this->assertSame(Http::STATUS_OK, $response->getStatus());
    }

    public function testSetPermissionReturns400WhenRemovingOwnAdmin(): void {
        // Current user is 'admin', trying to change own level to non-admin
        $existingPerm = new Permission();
        $existingPerm->setNcUserId('admin');
        $existingPerm->setLevel('admin');

        $this->permissionService->method('getUserPermission')
            ->with('admin')
            ->willReturn($existingPerm);

        // php://input is empty, so level defaults to 'none'
        $response = $this->controller->setPermission('admin');

        $this->assertSame(Http::STATUS_BAD_REQUEST, $response->getStatus());
        $this->assertStringContainsString('Admin', $response->getData()['error']);
    }

    public function testSetPermissionReturns400OnValidationException(): void {
        $this->permissionService->method('setPermission')
            ->willThrowException(new ValidationException('Invalid level'));

        $response = $this->controller->setPermission('otheruser');

        $this->assertSame(Http::STATUS_BAD_REQUEST, $response->getStatus());
    }

    public function testSetPermissionReturns500OnException(): void {
        $this->permissionService->method('setPermission')
            ->willThrowException(new \RuntimeException('fail'));

        $response = $this->controller->setPermission('otheruser');

        $this->assertSame(Http::STATUS_INTERNAL_SERVER_ERROR, $response->getStatus());
    }

    // ── DELETE /api/v1/permissions/{ncUserId} ──────────────────────

    public function testRemovePermissionReturns200OnSuccess(): void {
        $this->permissionService->expects($this->once())
            ->method('removePermission')->with('otheruser');

        $response = $this->controller->removePermission('otheruser');

        $this->assertSame(Http::STATUS_OK, $response->getStatus());
        $this->assertSame('removed', $response->getData()['status']);
    }

    public function testRemovePermissionReturns400WhenRemovingSelf(): void {
        // Current user is 'admin'
        $response = $this->controller->removePermission('admin');

        $this->assertSame(Http::STATUS_BAD_REQUEST, $response->getStatus());
        $this->assertStringContainsString('Eigene', $response->getData()['error']);
    }

    public function testRemovePermissionReturns500OnException(): void {
        $this->permissionService->method('removePermission')
            ->willThrowException(new \RuntimeException('fail'));

        $response = $this->controller->removePermission('otheruser');

        $this->assertSame(Http::STATUS_INTERNAL_SERVER_ERROR, $response->getStatus());
    }
}
