<?php

declare(strict_types=1);

namespace OCA\Mitgliederverwaltung\Tests\Unit;

use OCA\Mitgliederverwaltung\Db\Member;
use OCA\Mitgliederverwaltung\Db\MemberMapper;
use OCA\Mitgliederverwaltung\Service\MilestoneService;
use OCP\IConfig;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

class MilestoneServiceTest extends TestCase {

    private MilestoneService $service;
    private MemberMapper&MockObject $memberMapper;
    private IConfig&MockObject $config;

    protected function setUp(): void {
        $this->memberMapper = $this->createMock(MemberMapper::class);
        $this->config = $this->createMock(IConfig::class);

        $this->service = new MilestoneService(
            $this->memberMapper,
            $this->config
        );
    }

    private function createMemberEntity(int $id, string $eintritt, string $status = 'aktiv'): Member {
        $member = new Member();
        $member->setVorname('Max');
        $member->setNachname('Mueller');
        $member->setGeburtsdatum('1980-03-15');
        $member->setRolle('mitglied');
        $member->setEintritt($eintritt);
        $member->setStatus($status);
        $ref = new \ReflectionProperty($member, 'id');
        $ref->setAccessible(true);
        $ref->setValue($member, $id);
        return $member;
    }

    // ── getThresholds() ────────────────────────────────────────────

    public function testGetThresholdsReturnsDefault(): void {
        $this->config->method('getAppValue')
            ->willReturn('10,25,40,50,60,70');

        $result = $this->service->getThresholds();

        $this->assertEquals([10, 25, 40, 50, 60, 70], $result);
    }

    public function testGetThresholdsReturnsCustom(): void {
        $this->config->method('getAppValue')
            ->willReturn('5,15,25');

        $result = $this->service->getThresholds();

        $this->assertEquals([5, 15, 25], $result);
    }

    public function testGetThresholdsFiltersNonNumeric(): void {
        $this->config->method('getAppValue')
            ->willReturn('10,abc,25,,50');

        $result = $this->service->getThresholds();

        $this->assertEquals([10, 25, 50], array_values($result));
    }

    // ── setThresholds() ────────────────────────────────────────────

    public function testSetThresholdsSortsAndSaves(): void {
        $this->config->expects($this->once())
            ->method('setAppValue')
            ->with(
                'mitgliederverwaltung',
                'milestone_thresholds',
                '10,25,50'
            );

        $this->service->setThresholds([50, 10, 25]);
    }

    public function testSetThresholdsFiltersInvalid(): void {
        $this->config->expects($this->once())
            ->method('setAppValue')
            ->with(
                'mitgliederverwaltung',
                'milestone_thresholds',
                '10,25'
            );

        $this->service->setThresholds([10, -5, 0, 25]);
    }

    // ── getUpcomingMilestones() ────────────────────────────────────

    public function testGetUpcomingMilestonesFindsMatch(): void {
        $this->config->method('getAppValue')
            ->willReturn('10,25,50');

        // Member joined in 2000, checking year 2025 -> 25 years
        $member = $this->createMemberEntity(1, '2000-06-15');

        $this->memberMapper->method('findAll')->willReturn([$member]);

        $result = $this->service->getUpcomingMilestones(2025);

        $this->assertCount(1, $result);
        $this->assertEquals(25, $result[0]['mitgliedsjahre']);
        $this->assertEquals('2025-06-15', $result[0]['jubilaeumDatum']);
    }

    public function testGetUpcomingMilestonesNoMatch(): void {
        $this->config->method('getAppValue')
            ->willReturn('10,25,50');

        // Member joined in 2000, checking year 2023 -> 23 years (not a threshold)
        $member = $this->createMemberEntity(1, '2000-06-15');

        $this->memberMapper->method('findAll')->willReturn([$member]);

        $result = $this->service->getUpcomingMilestones(2023);

        $this->assertCount(0, $result);
    }

    public function testGetUpcomingMilestonesExcludesInactive(): void {
        $this->config->method('getAppValue')
            ->willReturn('10,25,50');

        $member = $this->createMemberEntity(1, '2000-06-15', 'inaktiv');

        $this->memberMapper->method('findAll')->willReturn([$member]);

        $result = $this->service->getUpcomingMilestones(2025);

        $this->assertCount(0, $result);
    }

    public function testGetUpcomingMilestonesIncludesInactiveWhenRequested(): void {
        $this->config->method('getAppValue')
            ->willReturn('10,25,50');

        $member = $this->createMemberEntity(1, '2000-06-15', 'inaktiv');

        $this->memberMapper->method('findAll')->willReturn([$member]);

        $result = $this->service->getUpcomingMilestones(2025, true);

        $this->assertCount(1, $result);
    }

    public function testGetUpcomingMilestonesSkipsMembersWithoutEintritt(): void {
        $this->config->method('getAppValue')
            ->willReturn('10,25,50');

        $member = new Member();
        $member->setVorname('Max');
        $member->setNachname('Mueller');
        $member->setGeburtsdatum('1980-01-01');
        $member->setRolle('mitglied');
        $member->setEintritt('');
        $member->setStatus('aktiv');
        $ref = new \ReflectionProperty($member, 'id');
        $ref->setAccessible(true);
        $ref->setValue($member, 1);

        $this->memberMapper->method('findAll')->willReturn([$member]);

        $result = $this->service->getUpcomingMilestones(2025);

        $this->assertCount(0, $result);
    }

    public function testGetUpcomingMilestonesSortsByDate(): void {
        $this->config->method('getAppValue')
            ->willReturn('25');

        $member1 = $this->createMemberEntity(1, '2000-11-20');
        $member1->setVorname('Anna');

        $member2 = $this->createMemberEntity(2, '2000-03-10');
        $member2->setVorname('Bob');

        $this->memberMapper->method('findAll')->willReturn([$member1, $member2]);

        $result = $this->service->getUpcomingMilestones(2025);

        $this->assertCount(2, $result);
        // Bob (March) should come before Anna (November)
        $this->assertLessThan($result[1]['jubilaeumDatum'], $result[0]['jubilaeumDatum']);
    }

    public function testGetUpcomingMilestonesContainsExpectedFields(): void {
        $this->config->method('getAppValue')
            ->willReturn('25');

        $member = $this->createMemberEntity(1, '2000-06-15');

        $this->memberMapper->method('findAll')->willReturn([$member]);

        $result = $this->service->getUpcomingMilestones(2025);

        $this->assertCount(1, $result);
        $milestone = $result[0];
        $this->assertArrayHasKey('memberId', $milestone);
        $this->assertArrayHasKey('vorname', $milestone);
        $this->assertArrayHasKey('nachname', $milestone);
        $this->assertArrayHasKey('eintritt', $milestone);
        $this->assertArrayHasKey('mitgliedsjahre', $milestone);
        $this->assertArrayHasKey('jubilaeumDatum', $milestone);
        $this->assertArrayHasKey('status', $milestone);
    }

    public function testGetUpcomingMilestonesEmptyList(): void {
        $this->config->method('getAppValue')
            ->willReturn('10,25,50');

        $this->memberMapper->method('findAll')->willReturn([]);

        $result = $this->service->getUpcomingMilestones(2025);

        $this->assertCount(0, $result);
    }
}
