<?php

declare(strict_types=1);

namespace OCA\Mitgliederverwaltung\Tests\Integration;

use OCA\Mitgliederverwaltung\Controller\MilestoneController;
use OCA\Mitgliederverwaltung\Service\MilestoneService;
use OCA\Mitgliederverwaltung\Service\PermissionService;
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 MilestoneController API endpoints.
 */
class MilestoneControllerTest extends TestCase {

    private MilestoneController $controller;
    private MilestoneService&MockObject $milestoneService;
    private PermissionService&MockObject $permissionService;
    private IUserSession&MockObject $userSession;
    private IRequest&MockObject $request;
    private LoggerInterface&MockObject $logger;

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

        $this->milestoneService = $this->createMock(MilestoneService::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 user
        $user = $this->createMock(IUser::class);
        $user->method('getUID')->willReturn('testuser');
        $this->userSession->method('getUser')->willReturn($user);

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

    // ── GET /api/v1/milestones ─────────────────────────────────────

    public function testIndexReturns200WithDefaultYear(): void {
        $this->request->method('getParam')->willReturn(null);
        $milestones = [['memberId' => 1, 'years' => 10]];
        $this->milestoneService->method('getUpcomingMilestones')
            ->willReturn($milestones);

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

        $this->assertSame(Http::STATUS_OK, $response->getStatus());
        $data = $response->getData();
        $this->assertArrayHasKey('data', $data);
        $this->assertArrayHasKey('year', $data);
        $this->assertArrayHasKey('count', $data);
        $this->assertSame(1, $data['count']);
    }

    public function testIndexReturns200WithSpecifiedYear(): void {
        $this->request->method('getParam')
            ->willReturnCallback(fn($name) => match ($name) {
                'year' => '2030',
                'includeInactive' => null,
                default => null,
            });

        $this->milestoneService->method('getUpcomingMilestones')
            ->with(2030, false)
            ->willReturn([]);

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

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

    public function testIndexReturns200WithIncludeInactive(): void {
        $this->request->method('getParam')
            ->willReturnCallback(fn($name) => match ($name) {
                'year' => '2026',
                'includeInactive' => 'true',
                default => null,
            });

        $this->milestoneService->method('getUpcomingMilestones')
            ->with(2026, true)
            ->willReturn([]);

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

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

    public function testIndexReturns500OnException(): void {
        $this->request->method('getParam')->willReturn(null);
        $this->milestoneService->method('getUpcomingMilestones')
            ->willThrowException(new \RuntimeException('DB error'));

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

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

    // ── GET /api/v1/milestones/settings ────────────────────────────

    public function testGetSettingsReturns200(): void {
        $this->milestoneService->method('getThresholds')
            ->willReturn([5, 10, 15, 20, 25]);

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

        $this->assertSame(Http::STATUS_OK, $response->getStatus());
        $this->assertArrayHasKey('thresholds', $response->getData());
        $this->assertSame([5, 10, 15, 20, 25], $response->getData()['thresholds']);
    }

    // ── PUT /api/v1/milestones/settings ────────────────────────────

    public function testUpdateSettingsReturns403ForNonAdmin(): void {
        $this->permissionService->method('isAdmin')->willReturn(false);

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

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

    public function testUpdateSettingsReturns200ForAdmin(): void {
        $this->permissionService->method('isAdmin')->willReturn(true);
        // php://input is empty so thresholds will be []
        $this->milestoneService->method('getThresholds')
            ->willReturn([5, 10, 25]);

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

        $this->assertSame(Http::STATUS_OK, $response->getStatus());
        $this->assertArrayHasKey('thresholds', $response->getData());
    }

    public function testUpdateSettingsReturns500OnException(): void {
        $this->permissionService->method('isAdmin')->willReturn(true);
        $this->milestoneService->method('setThresholds')
            ->willThrowException(new \RuntimeException('fail'));

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

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