<?php

declare(strict_types=1);

namespace App\Tests\Unit\Administration\Domain\Model\HomeworkRules;

use App\Administration\Domain\Exception\HomeworkRuleParamsInvalidException;
use App\Administration\Domain\Model\HomeworkRules\HomeworkRule;
use App\Administration\Domain\Model\HomeworkRules\RuleType;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class HomeworkRuleTest extends TestCase
{
    #[Test]
    public function createMinimumDelayRule(): void
    {
        $rule = new HomeworkRule(RuleType::MINIMUM_DELAY, ['days' => 3]);

        self::assertSame(RuleType::MINIMUM_DELAY, $rule->type);
        self::assertSame(3, $rule->params['days']);
    }

    #[Test]
    public function createNoMondayAfterRule(): void
    {
        $rule = new HomeworkRule(RuleType::NO_MONDAY_AFTER, ['day' => 'friday', 'time' => '12:00']);

        self::assertSame(RuleType::NO_MONDAY_AFTER, $rule->type);
        self::assertSame('friday', $rule->params['day']);
        self::assertSame('12:00', $rule->params['time']);
    }

    #[Test]
    public function throwsOnMissingParamsForMinimumDelay(): void
    {
        $this->expectException(HomeworkRuleParamsInvalidException::class);

        new HomeworkRule(RuleType::MINIMUM_DELAY, []);
    }

    #[Test]
    public function throwsOnMissingParamsForNoMondayAfter(): void
    {
        $this->expectException(HomeworkRuleParamsInvalidException::class);

        new HomeworkRule(RuleType::NO_MONDAY_AFTER, ['day' => 'friday']);
    }

    #[Test]
    public function equalsReturnsTrueForIdenticalRules(): void
    {
        $rule1 = new HomeworkRule(RuleType::MINIMUM_DELAY, ['days' => 3]);
        $rule2 = new HomeworkRule(RuleType::MINIMUM_DELAY, ['days' => 3]);

        self::assertTrue($rule1->equals($rule2));
    }

    #[Test]
    public function equalsReturnsFalseForDifferentParams(): void
    {
        $rule1 = new HomeworkRule(RuleType::MINIMUM_DELAY, ['days' => 3]);
        $rule2 = new HomeworkRule(RuleType::MINIMUM_DELAY, ['days' => 5]);

        self::assertFalse($rule1->equals($rule2));
    }

    #[Test]
    public function equalsReturnsFalseForDifferentTypes(): void
    {
        $rule1 = new HomeworkRule(RuleType::MINIMUM_DELAY, ['days' => 3]);
        $rule2 = new HomeworkRule(RuleType::NO_MONDAY_AFTER, ['day' => 'friday', 'time' => '12:00']);

        self::assertFalse($rule1->equals($rule2));
    }

    #[Test]
    public function toArrayReturnsCorrectFormat(): void
    {
        $rule = new HomeworkRule(RuleType::MINIMUM_DELAY, ['days' => 3]);
        $array = $rule->toArray();

        self::assertSame('minimum_delay', $array['type']);
        self::assertSame(['days' => 3], $array['params']);
    }

    #[Test]
    public function fromArrayCreatesRule(): void
    {
        $data = ['type' => 'minimum_delay', 'params' => ['days' => 3]];
        $rule = HomeworkRule::fromArray($data);

        self::assertSame(RuleType::MINIMUM_DELAY, $rule->type);
        self::assertSame(3, $rule->params['days']);
    }

    #[Test]
    public function roundTripArrayConversion(): void
    {
        $original = new HomeworkRule(RuleType::NO_MONDAY_AFTER, ['day' => 'friday', 'time' => '12:00']);
        $restored = HomeworkRule::fromArray($original->toArray());

        self::assertTrue($original->equals($restored));
    }

    #[Test]
    public function throwsOnZeroDays(): void
    {
        $this->expectException(HomeworkRuleParamsInvalidException::class);

        new HomeworkRule(RuleType::MINIMUM_DELAY, ['days' => 0]);
    }

    #[Test]
    public function throwsOnNegativeDays(): void
    {
        $this->expectException(HomeworkRuleParamsInvalidException::class);

        new HomeworkRule(RuleType::MINIMUM_DELAY, ['days' => -1]);
    }

    #[Test]
    public function throwsOnExcessiveDays(): void
    {
        $this->expectException(HomeworkRuleParamsInvalidException::class);

        new HomeworkRule(RuleType::MINIMUM_DELAY, ['days' => 31]);
    }

    #[Test]
    public function throwsOnStringDays(): void
    {
        $this->expectException(HomeworkRuleParamsInvalidException::class);

        new HomeworkRule(RuleType::MINIMUM_DELAY, ['days' => 'three']);
    }

    #[Test]
    public function throwsOnInvalidDayName(): void
    {
        $this->expectException(HomeworkRuleParamsInvalidException::class);

        new HomeworkRule(RuleType::NO_MONDAY_AFTER, ['day' => 'firday', 'time' => '12:00']);
    }

    #[Test]
    public function throwsOnInvalidTimeFormat(): void
    {
        $this->expectException(HomeworkRuleParamsInvalidException::class);

        new HomeworkRule(RuleType::NO_MONDAY_AFTER, ['day' => 'friday', 'time' => 'banana']);
    }

    #[Test]
    public function acceptsBoundaryDays(): void
    {
        $rule1 = new HomeworkRule(RuleType::MINIMUM_DELAY, ['days' => 1]);
        $rule30 = new HomeworkRule(RuleType::MINIMUM_DELAY, ['days' => 30]);

        self::assertSame(1, $rule1->params['days']);
        self::assertSame(30, $rule30->params['days']);
    }
}
