<?php

declare(strict_types=1);

namespace OCA\Mitgliederverwaltung\Tests\Integration;

use OCA\Mitgliederverwaltung\Controller\DsgvoController;
use OCA\Mitgliederverwaltung\Service\DsgvoService;
use OCA\Mitgliederverwaltung\Service\EncryptedExportService;
use OCA\Mitgliederverwaltung\Service\PermissionService;
use OCA\Mitgliederverwaltung\Service\ValidationException;
use OCA\Mitgliederverwaltung\Tests\TestInputStream;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
use OCP\IRequest;
use OCP\IUser;
use OCP\IUserSession;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;

/**
 * Integration-level tests for DsgvoController API endpoints.
 */
class DsgvoControllerTest extends TestCase {

    private DsgvoController $controller;
    private DsgvoService&MockObject $dsgvoService;
    private EncryptedExportService&MockObject $encryptedService;
    private PermissionService&MockObject $permissionService;
    private IUserSession&MockObject $userSession;
    private IRequest&MockObject $request;
    private LoggerInterface&MockObject $logger;

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

        $this->dsgvoService = $this->createMock(DsgvoService::class);
        $this->encryptedService = $this->createMock(EncryptedExportService::class);
        $this->permissionService = $this->createMock(PermissionService::class);
        $this->userSession = $this->createMock(IUserSession::class);
        $this->request = $this->createMock(IRequest::class);
        $this->logger = $this->createMock(LoggerInterface::class);

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

        $this->controller = new DsgvoController(
            'mitgliederverwaltung',
            $this->request,
            $this->dsgvoService,
            $this->encryptedService,
            $this->permissionService,
            $this->userSession,
            $this->logger
        );
    }

    protected function tearDown(): void {
        TestInputStream::unregister();
        parent::tearDown();
    }

    private function setRequestBody(array $data): void {
        TestInputStream::register();
        TestInputStream::setData(json_encode($data));
    }

    // ── POST /api/v1/members/{id}/dsgvo-export ─────────────────────

    public function testExportReturnsDsgvoDataOnSuccess(): void {
        $this->setRequestBody(['password' => 'secret123']);

        $this->dsgvoService->method('exportMemberData')
            ->with(1)
            ->willReturn(['id' => 1, 'vorname' => 'Max']);

        $this->encryptedService->method('createEncryptedExport')
            ->willReturn([
                'content' => 'zipcontent',
                'filename' => 'dsgvo_export.zip',
            ]);

        $response = $this->controller->export(1);

        // DataDownloadResponse extends Response, status is 200 by default
        $this->assertSame(Http::STATUS_OK, $response->getStatus());
    }

    public function testExportReturns400WhenPasswordMissing(): void {
        $this->setRequestBody([]);

        $response = $this->controller->export(1);

        $this->assertInstanceOf(\OCP\AppFramework\Http\JSONResponse::class, $response);
        $this->assertSame(Http::STATUS_BAD_REQUEST, $response->getStatus());
    }

    public function testExportReturns400WhenPasswordEmpty(): void {
        $this->setRequestBody(['password' => '']);

        $response = $this->controller->export(1);

        $this->assertInstanceOf(\OCP\AppFramework\Http\JSONResponse::class, $response);
        $this->assertSame(Http::STATUS_BAD_REQUEST, $response->getStatus());
    }

    public function testExportReturns404ForMissingMember(): void {
        $this->setRequestBody(['password' => 'secret']);

        $this->dsgvoService->method('exportMemberData')
            ->willThrowException(new DoesNotExistException(''));

        $response = $this->controller->export(9999);

        $this->assertInstanceOf(\OCP\AppFramework\Http\JSONResponse::class, $response);
        $this->assertSame(Http::STATUS_NOT_FOUND, $response->getStatus());
    }

    public function testExportReturns403ForNonAdmin(): void {
        $this->permissionService = $this->createMock(PermissionService::class);
        $this->permissionService->method('isAdmin')->willReturn(false);

        $this->controller = new DsgvoController(
            'mitgliederverwaltung',
            $this->request,
            $this->dsgvoService,
            $this->encryptedService,
            $this->permissionService,
            $this->userSession,
            $this->logger
        );

        $this->setRequestBody(['password' => 'secret']);

        $response = $this->controller->export(1);

        $this->assertInstanceOf(\OCP\AppFramework\Http\JSONResponse::class, $response);
        $this->assertSame(Http::STATUS_FORBIDDEN, $response->getStatus());
    }

    public function testExportReturns500OnInternalError(): void {
        $this->setRequestBody(['password' => 'secret']);

        $this->dsgvoService->method('exportMemberData')
            ->willThrowException(new \RuntimeException('DB error'));

        $response = $this->controller->export(1);

        $this->assertInstanceOf(\OCP\AppFramework\Http\JSONResponse::class, $response);
        $this->assertSame(Http::STATUS_INTERNAL_SERVER_ERROR, $response->getStatus());
    }

    // ── DELETE /api/v1/members/{id}/dsgvo-delete ───────────────────

    public function testHardDeleteReturns200OnSuccess(): void {
        $this->setRequestBody(['confirm' => true]);

        $this->dsgvoService->expects($this->once())->method('hardDeleteMember')->with(1);

        $response = $this->controller->hardDelete(1);

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

    public function testHardDeleteReturns400WithoutConfirmation(): void {
        $this->setRequestBody(['confirm' => false]);

        $response = $this->controller->hardDelete(1);

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

    public function testHardDeleteReturns400WhenConfirmNull(): void {
        $this->setRequestBody([]);

        $response = $this->controller->hardDelete(1);

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

    public function testHardDeleteReturns404ForMissingMember(): void {
        $this->setRequestBody(['confirm' => true]);

        $this->dsgvoService->method('hardDeleteMember')
            ->willThrowException(new DoesNotExistException(''));

        $response = $this->controller->hardDelete(9999);

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

    public function testHardDeleteReturns403ForNonAdmin(): void {
        $this->permissionService = $this->createMock(PermissionService::class);
        $this->permissionService->method('isAdmin')->willReturn(false);

        $this->controller = new DsgvoController(
            'mitgliederverwaltung',
            $this->request,
            $this->dsgvoService,
            $this->encryptedService,
            $this->permissionService,
            $this->userSession,
            $this->logger
        );

        $this->setRequestBody(['confirm' => true]);

        $response = $this->controller->hardDelete(1);

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

    public function testHardDeleteReturns500OnInternalError(): void {
        $this->setRequestBody(['confirm' => true]);

        $this->dsgvoService->method('hardDeleteMember')
            ->willThrowException(new \RuntimeException('DB error'));

        $response = $this->controller->hardDelete(1);

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

    public function testHardDeleteAcceptsConfirmAsBoolean(): void {
        $this->setRequestBody(['confirm' => true]);

        $this->dsgvoService->expects($this->once())->method('hardDeleteMember')->with(1);

        $response = $this->controller->hardDelete(1);

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

    public function testHardDeleteAcceptsConfirmAs1(): void {
        $this->setRequestBody(['confirm' => '1']);

        $this->dsgvoService->expects($this->once())->method('hardDeleteMember')->with(1);

        $response = $this->controller->hardDelete(1);

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