<?php
declare(strict_types=1);

namespace Tests\Unit\Core;

use App\Core\Validator;
use Tests\TestCase;

class ValidatorTest extends TestCase
{
    private Validator $validator;

    protected function setUp(): void
    {
        parent::setUp();
        $this->validator = new Validator();
    }

    public function testRequiredPassesWithValue(): void
    {
        $result = $this->validator->validate(['name' => 'test'], ['name' => 'required']);
        $this->assertTrue($result->passes());
        $this->assertSame(['name' => 'test'], $result->validated());
    }

    public function testRequiredFailsWithEmpty(): void
    {
        $result = $this->validator->validate(['name' => ''], ['name' => 'required']);
        $this->assertTrue($result->fails());
        $this->assertArrayHasKey('name', $result->errors());
    }

    public function testRequiredFailsWithNull(): void
    {
        $result = $this->validator->validate([], ['name' => 'required']);
        $this->assertTrue($result->fails());
    }

    public function testStringPasses(): void
    {
        $result = $this->validator->validate(['name' => 'hello'], ['name' => 'string']);
        $this->assertTrue($result->passes());
    }

    public function testIntegerPasses(): void
    {
        $result = $this->validator->validate(['age' => '25'], ['age' => 'integer']);
        $this->assertTrue($result->passes());
    }

    public function testIntegerFailsWithFloat(): void
    {
        $result = $this->validator->validate(['age' => '25.5'], ['age' => 'integer']);
        $this->assertTrue($result->fails());
    }

    public function testNumericPasses(): void
    {
        $result = $this->validator->validate(['price' => '99.99'], ['price' => 'numeric']);
        $this->assertTrue($result->passes());
    }

    public function testEmailPasses(): void
    {
        $result = $this->validator->validate(['email' => 'test@example.com'], ['email' => 'email']);
        $this->assertTrue($result->passes());
    }

    public function testEmailFails(): void
    {
        $result = $this->validator->validate(['email' => 'not-an-email'], ['email' => 'email']);
        $this->assertTrue($result->fails());
    }

    public function testDatePasses(): void
    {
        $result = $this->validator->validate(['dob' => '1990-05-15'], ['dob' => 'date']);
        $this->assertTrue($result->passes());
    }

    public function testDateFailsInvalid(): void
    {
        $result = $this->validator->validate(['dob' => '2024-13-45'], ['dob' => 'date']);
        $this->assertTrue($result->fails());
    }

    public function testMinStringLength(): void
    {
        $result = $this->validator->validate(['name' => 'ab'], ['name' => 'min:3']);
        $this->assertTrue($result->fails());
    }

    public function testMaxStringLength(): void
    {
        $result = $this->validator->validate(['name' => 'abcdef'], ['name' => 'max:5']);
        $this->assertTrue($result->fails());
    }

    public function testInPasses(): void
    {
        $result = $this->validator->validate(['status' => 'active'], ['status' => 'in:active,inactive']);
        $this->assertTrue($result->passes());
    }

    public function testInFails(): void
    {
        $result = $this->validator->validate(['status' => 'deleted'], ['status' => 'in:active,inactive']);
        $this->assertTrue($result->fails());
    }

    public function testNullableSkipsValidation(): void
    {
        $result = $this->validator->validate(['phone' => ''], ['phone' => 'nullable|phone_eg']);
        $this->assertTrue($result->passes());
    }

    public function testNullableAllowsNull(): void
    {
        $result = $this->validator->validate([], ['phone' => 'nullable|phone_eg']);
        $this->assertTrue($result->passes());
    }

    public function testPhoneEgPasses(): void
    {
        $result = $this->validator->validate(['phone' => '01012345678'], ['phone' => 'phone_eg']);
        $this->assertTrue($result->passes());
    }

    public function testPhoneEgFails(): void
    {
        $result = $this->validator->validate(['phone' => '123'], ['phone' => 'phone_eg']);
        $this->assertTrue($result->fails());
    }

    public function testNationalIdPasses(): void
    {
        $result = $this->validator->validate(['nid' => '29005151234567'], ['nid' => 'national_id']);
        $this->assertTrue($result->passes());
    }

    public function testNationalIdFailsWrongLength(): void
    {
        $result = $this->validator->validate(['nid' => '123'], ['nid' => 'national_id']);
        $this->assertTrue($result->fails());
    }

    public function testBetweenPasses(): void
    {
        $result = $this->validator->validate(['age' => '25'], ['age' => 'between:18,60']);
        $this->assertTrue($result->passes());
    }

    public function testBetweenFails(): void
    {
        $result = $this->validator->validate(['age' => '10'], ['age' => 'between:18,60']);
        $this->assertTrue($result->fails());
    }

    public function testConfirmedPasses(): void
    {
        $data = ['password' => 'secret', 'password_confirmation' => 'secret'];
        $result = $this->validator->validate($data, ['password' => 'confirmed']);
        $this->assertTrue($result->passes());
    }

    public function testConfirmedFails(): void
    {
        $data = ['password' => 'secret', 'password_confirmation' => 'different'];
        $result = $this->validator->validate($data, ['password' => 'confirmed']);
        $this->assertTrue($result->fails());
    }

    public function testMultipleRulesAllPass(): void
    {
        $result = $this->validator->validate(['name' => 'Ahmed'], ['name' => 'required|string|min:2|max:50']);
        $this->assertTrue($result->passes());
    }

    public function testMultipleFieldsValidation(): void
    {
        $data = ['name' => 'Ahmed', 'email' => 'bad'];
        $rules = ['name' => 'required|string', 'email' => 'required|email'];
        $result = $this->validator->validate($data, $rules);
        $this->assertTrue($result->fails());
        $this->assertArrayNotHasKey('name', $result->errors());
        $this->assertArrayHasKey('email', $result->errors());
    }

    public function testDigitsPasses(): void
    {
        $result = $this->validator->validate(['code' => '1234'], ['code' => 'digits:4']);
        $this->assertTrue($result->passes());
    }

    public function testDigitsFails(): void
    {
        $result = $this->validator->validate(['code' => '12'], ['code' => 'digits:4']);
        $this->assertTrue($result->fails());
    }
}
