<?php

namespace Tests\Unit;

use App\Support\EmployeeValidation;
use App\Support\FormTextRules;
use Carbon\Carbon;
use Illuminate\Support\Facades\Validator;
use Tests\TestCase;

class RecruitmentValidationTest extends TestCase
{
    public function test_job_title_rejects_numeric_only(): void
    {
        $validator = Validator::make(
            ['title' => '12345'],
            ['title' => FormTextRules::notNumericOnly(true, 191)],
            FormTextRules::messages()
        );

        $this->assertTrue($validator->fails());
        $this->assertTrue($validator->errors()->has('title'));
    }

    public function test_job_department_is_required(): void
    {
        $validator = Validator::make(
            ['department_id' => ''],
            ['department_id' => ['required', 'exists:departments,id']],
            FormTextRules::messages()
        );

        $this->assertTrue($validator->fails());
        $this->assertTrue($validator->errors()->has('department_id'));
        $this->assertSame(
            'Please select a department.',
            $validator->errors()->first('department_id')
        );
    }

    public function test_job_location_and_description_reject_numeric_only(): void
    {
        $location = Validator::make(
            ['location' => '999'],
            ['location' => FormTextRules::notNumericOnly(false, 191)],
            FormTextRules::messages()
        );
        $this->assertTrue($location->fails());

        $description = Validator::make(
            ['description' => '42'],
            ['description' => FormTextRules::notNumericOnly(false, 5000)],
            FormTextRules::messages()
        );
        $this->assertTrue($description->fails());
    }

    public function test_applicant_name_rejects_digits(): void
    {
        $validator = Validator::make(
            ['name' => '12345'],
            ['name' => ['required', 'string', 'max:191', 'regex:'.EmployeeValidation::ALPHA_SPACES]],
            ['name.regex' => EmployeeValidation::messages()['name.regex']]
        );

        $this->assertTrue($validator->fails());
        $this->assertTrue($validator->errors()->has('name'));
    }

    public function test_applicant_phone_rejects_short_and_negative_values(): void
    {
        $short = Validator::make(
            ['phone' => '123'],
            ['phone' => ['nullable', 'digits:10']],
            ['phone.digits' => EmployeeValidation::messages()['phone.digits']]
        );
        $this->assertTrue($short->fails());

        $negative = Validator::make(
            ['phone' => '-987654321'],
            ['phone' => ['nullable', 'digits:10']],
            ['phone.digits' => EmployeeValidation::messages()['phone.digits']]
        );
        $this->assertTrue($negative->fails());
    }

    public function test_applicant_notes_reject_numeric_only(): void
    {
        $validator = Validator::make(
            ['notes' => '98765'],
            ['notes' => FormTextRules::notNumericOnly(false, 5000)],
            FormTextRules::messages()
        );

        $this->assertTrue($validator->fails());
        $this->assertTrue($validator->errors()->has('notes'));
    }

    public function test_resume_is_required_on_create_rules(): void
    {
        $validator = Validator::make(
            [],
            ['resume' => ['required', 'file', 'mimes:pdf,doc,docx', 'max:5120']],
            FormTextRules::messages()
        );

        $this->assertTrue($validator->fails());
        $this->assertTrue($validator->errors()->has('resume'));
        $this->assertSame('Please upload a resume.', $validator->errors()->first('resume'));
    }

    public function test_applied_at_before_posted_at_is_rejected(): void
    {
        $postedAt = Carbon::parse('2026-06-15')->startOfDay();
        $appliedAt = Carbon::parse('2026-06-01')->startOfDay();

        $this->assertTrue($appliedAt->lt($postedAt));

        $validator = Validator::make(
            [
                'job_posting_id' => 1,
                'applied_at' => '2026-06-01',
            ],
            [
                'job_posting_id' => ['required'],
                'applied_at' => ['nullable', 'date'],
            ],
            FormTextRules::messages()
        );

        $validator->after(function ($validator) use ($postedAt): void {
            $appliedAt = $validator->getData()['applied_at'] ?? null;
            if (! $appliedAt) {
                return;
            }

            if (Carbon::parse($appliedAt)->startOfDay()->lt($postedAt)) {
                $validator->errors()->add(
                    'applied_at',
                    FormTextRules::messages()['applied_at.after_or_equal']
                );
            }
        });

        $this->assertTrue($validator->fails());
        $this->assertTrue($validator->errors()->has('applied_at'));
        $this->assertSame(
            'The applied date must be on or after the job posting date.',
            $validator->errors()->first('applied_at')
        );
    }

    public function test_email_normalize_lowercases(): void
    {
        $normalized = EmployeeValidation::normalize(['email' => '  Test.User@Example.COM ']);
        $this->assertSame('test.user@example.com', $normalized['email']);
    }

    public function test_recruitment_messages_exist(): void
    {
        $messages = FormTextRules::messages();
        $this->assertArrayHasKey('location.not_regex', $messages);
        $this->assertArrayHasKey('department_id.required', $messages);
        $this->assertArrayHasKey('job_posting_id.required', $messages);
        $this->assertArrayHasKey('resume.required', $messages);
        $this->assertArrayHasKey('applied_at.after_or_equal', $messages);
    }

    public function test_refer_resume_is_required(): void
    {
        $validator = Validator::make(
            [],
            ['resume' => ['required', 'file', 'mimes:pdf,doc,docx', 'max:5120']],
            FormTextRules::messages()
        );

        $this->assertTrue($validator->fails());
        $this->assertTrue($validator->errors()->has('resume'));
        $this->assertSame('Please upload a resume.', $validator->errors()->first('resume'));
    }

    public function test_refer_name_rejects_digits(): void
    {
        $validator = Validator::make(
            ['name' => '12345'],
            ['name' => ['required', 'string', 'max:191', 'regex:'.EmployeeValidation::ALPHA_SPACES]],
            ['name.regex' => EmployeeValidation::messages()['name.regex']]
        );

        $this->assertTrue($validator->fails());
        $this->assertTrue($validator->errors()->has('name'));
    }

    public function test_refer_phone_requires_ten_digits(): void
    {
        $validator = Validator::make(
            ['phone' => '123'],
            ['phone' => ['nullable', 'digits:10']],
            ['phone.digits' => EmployeeValidation::messages()['phone.digits']]
        );

        $this->assertTrue($validator->fails());
        $this->assertTrue($validator->errors()->has('phone'));
    }

    public function test_edit_applicant_resume_required_when_no_stored_path(): void
    {
        $rules = $this->editApplicantResumeRules(resumeRequired: true);

        $validator = Validator::make([], $rules, FormTextRules::messages());

        $this->assertTrue($validator->fails());
        $this->assertTrue($validator->errors()->has('resume'));
        $this->assertSame('Please upload a resume.', $validator->errors()->first('resume'));
    }

    public function test_edit_applicant_resume_optional_when_stored_path_exists(): void
    {
        $rules = $this->editApplicantResumeRules(resumeRequired: false);

        $validator = Validator::make([], $rules, FormTextRules::messages());

        $this->assertFalse($validator->errors()->has('resume'));
    }

    /**
     * @return array<string, list<string>>
     */
    private function editApplicantResumeRules(bool $resumeRequired): array
    {
        return [
            'resume' => array_values(array_filter([
                $resumeRequired ? 'required' : 'nullable',
                'file',
                'mimes:pdf,doc,docx',
                'max:5120',
            ])),
        ];
    }
}
