<?php

namespace Tests\Unit;

use App\Http\Controllers\Web\AttendanceController;
use App\Models\Attendance;
use App\Models\User;
use App\Services\Attendance\AttendanceCalendarDayResolver;
use App\Services\Attendance\AttendancePunchValidator;
use App\Services\Attendance\AttendanceRecordWriter;
use App\Services\Attendance\WeekoffResolver;
use Mockery;
use ReflectionMethod;
use Tests\TestCase;

class AttendanceEmploymentWindowTest extends TestCase
{
    protected function tearDown(): void
    {
        Mockery::close();
        parent::tearDown();
    }

    public function test_dates_before_joining_are_outside_employment(): void
    {
        $controller = $this->controller();
        $user = new User(['joining_date' => '2026-09-21']);

        $this->assertTrue($this->invoke($controller, 'isDateOutsideEmployment', [$user, '2026-09-20']));
        $this->assertFalse($this->invoke($controller, 'isDateOutsideEmployment', [$user, '2026-09-21']));
        $this->assertFalse($this->invoke($controller, 'isDateOutsideEmployment', [$user, '2026-09-22']));
    }

    public function test_dates_after_last_working_are_outside_employment(): void
    {
        $controller = $this->controller();
        $user = new User([
            'joining_date' => '2026-09-01',
            'last_working_date' => '2026-09-15',
        ]);

        $this->assertFalse($this->invoke($controller, 'isDateOutsideEmployment', [$user, '2026-09-15']));
        $this->assertTrue($this->invoke($controller, 'isDateOutsideEmployment', [$user, '2026-09-16']));
    }

    public function test_employment_aware_status_returns_not_joined_without_punch(): void
    {
        $controller = $this->controller();
        $user = new User(['joining_date' => '2026-09-21']);

        $status = $this->invoke($controller, 'resolveEmploymentAwareStatus', [
            $user,
            '2026-09-10',
            false,
            [],
            [],
            [],
            null,
        ]);

        $this->assertSame('not_joined', $status);
    }

    public function test_employment_aware_status_returns_not_joined_even_when_punch_exists_before_join(): void
    {
        $controller = $this->controller();
        $user = new User(['joining_date' => '2026-09-21']);
        $attendance = new Attendance([
            'clock_in_date_time' => '2026-09-10 09:00:00',
            'is_half_day' => false,
            'status' => 'present',
        ]);

        $status = $this->invoke($controller, 'resolveEmploymentAwareStatus', [
            $user,
            '2026-09-10',
            false,
            [],
            ['2026-09-10' => $attendance],
            [],
            null,
        ]);

        $this->assertSame('not_joined', $status);
    }

    protected function controller(): AttendanceController
    {
        return new AttendanceController(
            Mockery::mock(WeekoffResolver::class),
            new AttendanceCalendarDayResolver,
            Mockery::mock(AttendancePunchValidator::class),
            Mockery::mock(AttendanceRecordWriter::class)
        );
    }

    /**
     * @param  array<int, mixed>  $args
     */
    protected function invoke(object $object, string $method, array $args = []): mixed
    {
        $ref = new ReflectionMethod($object, $method);
        $ref->setAccessible(true);

        return $ref->invokeArgs($object, $args);
    }
}
