<?php

declare(strict_types=1);

namespace Tests\Feature\Employees;

use App\Enums\AuditEvent;
use App\Models\AuditLog;
use App\Models\Employee;
use App\Models\Location;
use App\Models\Team;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;

class EmployeeManagementTest extends TestCase
{
    use RefreshDatabase;

    // -------------------------------------------------------------- access

    #[Test]
    public function a_guest_cannot_reach_employees(): void
    {
        $this->get(route('employees.index'))->assertRedirect(route('login'));
    }

    #[Test]
    public function any_signed_in_user_can_view_create_and_edit_employees(): void
    {
        // Employee Management is not master-only: everyday work is open to
        // both user types. Only deletion and restoration are restricted.
        $user = User::factory()->create();
        $employee = Employee::factory()->create();

        $this->actingAs($user)->get(route('employees.index'))->assertOk();
        $this->actingAs($user)->get(route('employees.create'))->assertOk();
        $this->actingAs($user)->get(route('employees.show', $employee))->assertOk();
        $this->actingAs($user)->get(route('employees.edit', $employee))->assertOk();

        $this->actingAs($user)
            ->post(route('employees.store'), [
                'employee_code' => 'AUM00001',
                'first_name' => 'Priya',
                'last_name' => 'Sharma',
                'status' => 'active',
            ])
            ->assertRedirect();

        $this->assertDatabaseHas('employees', ['employee_code' => 'AUM00001']);
    }

    #[Test]
    public function a_regular_user_cannot_delete_or_restore(): void
    {
        $user = User::factory()->create();
        $employee = Employee::factory()->create();

        $this->actingAs($user)
            ->delete(route('employees.destroy', $employee))
            ->assertForbidden();

        $employee->delete();

        $this->actingAs($user)
            ->patch(route('employees.restore', $employee))
            ->assertForbidden();

        $this->assertSoftDeleted('employees', ['id' => $employee->getKey()]);
    }

    // ------------------------------------------------------------- listing

    #[Test]
    public function the_listing_can_be_searched_by_name(): void
    {
        Employee::factory()->create(['first_name' => 'Priya', 'last_name' => 'Sharma']);
        Employee::factory()->create(['first_name' => 'Rahul', 'last_name' => 'Verma']);

        $this->actingAs(User::factory()->create())
            ->get(route('employees.index', ['search' => 'priya']))
            ->assertOk()
            ->assertSee('Priya Sharma')
            ->assertDontSee('Rahul Verma');
    }

    #[Test]
    public function the_listing_can_be_searched_by_employee_code(): void
    {
        Employee::factory()->create(['employee_code' => 'AUM10001', 'first_name' => 'Priya']);
        Employee::factory()->create(['employee_code' => 'AUM10002', 'first_name' => 'Rahul']);

        $this->actingAs(User::factory()->create())
            ->get(route('employees.index', ['search' => 'AUM10001']))
            ->assertOk()
            ->assertSee('Priya')
            ->assertDontSee('Rahul');
    }

    #[Test]
    public function the_listing_can_be_filtered_by_status(): void
    {
        Employee::factory()->create(['first_name' => 'Priya']);
        Employee::factory()->exited()->create(['first_name' => 'Rahul']);

        $this->actingAs(User::factory()->create())
            ->get(route('employees.index', ['status' => 'exited']))
            ->assertOk()
            ->assertSee('Rahul')
            ->assertDontSee('Priya');
    }

    #[Test]
    public function the_listing_can_be_filtered_by_team(): void
    {
        $engineering = Team::factory()->create(['name' => 'Engineering']);
        $hr = Team::factory()->create(['name' => 'Human Resources']);

        Employee::factory()->inTeam($engineering)->create(['first_name' => 'Priya']);
        Employee::factory()->inTeam($hr)->create(['first_name' => 'Rahul']);

        $this->actingAs(User::factory()->create())
            ->get(route('employees.index', ['team' => $engineering->getKey()]))
            ->assertOk()
            ->assertSee('Priya')
            ->assertDontSee('Rahul');
    }

    #[Test]
    public function the_listing_can_be_filtered_by_squad(): void
    {
        Employee::factory()->inSquad('Platform')->create(['first_name' => 'Priya']);
        Employee::factory()->inSquad('Growth')->create(['first_name' => 'Rahul']);

        $this->actingAs(User::factory()->create())
            ->get(route('employees.index', ['squad' => 'Platform']))
            ->assertOk()
            ->assertSee('Priya')
            ->assertDontSee('Rahul');
    }

    #[Test]
    public function a_soft_deleted_employee_disappears_from_the_default_listing(): void
    {
        $employee = Employee::factory()->create(['first_name' => 'Priya']);
        $employee->delete();

        $this->actingAs(User::factory()->create())
            ->get(route('employees.index'))
            ->assertOk()
            ->assertDontSee('Priya');

        $this->actingAs(User::factory()->create())
            ->get(route('employees.index', ['trashed' => 1]))
            ->assertOk()
            ->assertSee('Priya');
    }

    // -------------------------------------------------------------- create

    #[Test]
    public function creating_an_employee_validates_its_input(): void
    {
        $this->actingAs(User::factory()->create())
            ->post(route('employees.store'), [])
            ->assertSessionHasErrors(['employee_code', 'first_name', 'status']);
    }

    #[Test]
    public function a_duplicate_employee_code_is_rejected(): void
    {
        Employee::factory()->create(['employee_code' => 'AUM10001']);

        $this->actingAs(User::factory()->create())
            ->post(route('employees.store'), [
                'employee_code' => 'AUM10001',
                'first_name' => 'Rahul',
                'status' => 'active',
            ])
            ->assertSessionHasErrors('employee_code');
    }

    #[Test]
    public function a_duplicate_email_is_rejected(): void
    {
        Employee::factory()->create(['email' => 'priya@aumnitechworks.com']);

        $this->actingAs(User::factory()->create())
            ->post(route('employees.store'), [
                'employee_code' => 'AUM10099',
                'first_name' => 'Rahul',
                'email' => 'priya@aumnitechworks.com',
                'status' => 'active',
            ])
            ->assertSessionHasErrors('email');
    }

    #[Test]
    public function an_employee_can_be_created_with_a_squad(): void
    {
        $this->actingAs(User::factory()->create())
            ->post(route('employees.store'), [
                'employee_code' => 'AUM10050',
                'first_name' => 'Priya',
                'last_name' => 'Sharma',
                'squad' => 'Platform',
                'status' => 'active',
            ])
            ->assertRedirect();

        $this->assertDatabaseHas('employees', [
            'employee_code' => 'AUM10050',
            'squad' => 'Platform',
        ]);
    }

    #[Test]
    public function creating_an_employee_is_audited(): void
    {
        $this->actingAs($actor = User::factory()->create())
            ->post(route('employees.store'), [
                'employee_code' => 'AUM10050',
                'first_name' => 'Priya',
                'status' => 'active',
            ]);

        $employee = Employee::firstWhere('employee_code', 'AUM10050');

        $this->assertNotNull($employee);
        $this->assertEquals($actor->getKey(), $employee->created_by);

        $this->assertDatabaseHas('audit_logs', [
            'event' => AuditEvent::Created->value,
            'auditable_type' => Employee::class,
            'auditable_id' => $employee->getKey(),
        ]);
    }

    // -------------------------------------------------------------- update

    #[Test]
    public function an_employee_can_be_edited(): void
    {
        $employee = Employee::factory()->create(['first_name' => 'Priya']);

        $this->actingAs(User::factory()->create())
            ->put(route('employees.update', $employee), [
                'employee_code' => $employee->employee_code,
                'first_name' => 'Priyanka',
                'status' => 'active',
            ])
            ->assertRedirect(route('employees.show', $employee));

        $this->assertDatabaseHas('employees', [
            'id' => $employee->getKey(),
            'first_name' => 'Priyanka',
        ]);
    }

    #[Test]
    public function editing_an_employee_is_audited_with_a_diff(): void
    {
        $employee = Employee::factory()->create(['first_name' => 'Priya']);

        $this->actingAs(User::factory()->create())
            ->put(route('employees.update', $employee), [
                'employee_code' => $employee->employee_code,
                'first_name' => 'Priyanka',
                'status' => 'active',
            ]);

        $log = AuditLog::query()
            ->where('auditable_type', Employee::class)
            ->where('auditable_id', $employee->getKey())
            ->where('event', AuditEvent::Updated->value)
            ->first();

        $this->assertNotNull($log);
        $this->assertSame('Priya', $log->old_values['first_name'] ?? null);
        $this->assertSame('Priyanka', $log->new_values['first_name'] ?? null);
    }

    // ------------------------------------------------------- delete/restore

    #[Test]
    public function a_master_can_delete_and_restore_an_employee(): void
    {
        $employee = Employee::factory()->create();

        $this->actingAs(User::factory()->master()->create())
            ->delete(route('employees.destroy', $employee))
            ->assertRedirect(route('employees.index'));

        $this->assertSoftDeleted('employees', ['id' => $employee->getKey()]);

        $this->actingAs(User::factory()->master()->create())
            ->patch(route('employees.restore', $employee))
            ->assertRedirect(route('employees.index'));

        $this->assertNotSoftDeleted('employees', ['id' => $employee->getKey()]);
    }

    #[Test]
    public function deleting_an_employee_is_audited(): void
    {
        $employee = Employee::factory()->create();

        $this->actingAs(User::factory()->master()->create())
            ->delete(route('employees.destroy', $employee));

        $this->assertDatabaseHas('audit_logs', [
            'event' => AuditEvent::Deleted->value,
            'auditable_type' => Employee::class,
            'auditable_id' => $employee->getKey(),
        ]);
    }

    // --------------------------------------------------- master-data guards

    #[Test]
    public function a_team_with_employees_cannot_be_deleted(): void
    {
        $team = Team::factory()->create(['name' => 'Engineering']);
        Employee::factory()->inTeam($team)->create();

        $response = $this->actingAs(User::factory()->master()->create())
            ->delete(route('masters.teams.destroy', $team));

        $response->assertRedirect(route('masters.teams.index'));
        $response->assertSessionHas('error');
        $this->assertStringContainsString('has 1 employee and cannot be deleted', (string) session('error'));
        $this->assertNotSoftDeleted('teams', ['id' => $team->getKey()]);
    }

    #[Test]
    public function a_location_with_employees_cannot_be_deleted(): void
    {
        $location = Location::factory()->create(['name' => 'Pune HQ']);
        Employee::factory()->atLocation($location)->create();

        $response = $this->actingAs(User::factory()->master()->create())
            ->delete(route('masters.locations.destroy', $location));

        $response->assertRedirect(route('masters.locations.index'));
        $response->assertSessionHas('error');
        $this->assertNotSoftDeleted('locations', ['id' => $location->getKey()]);
    }

    #[Test]
    public function a_team_can_be_given_a_head_employee(): void
    {
        $team = Team::factory()->create(['name' => 'Engineering']);
        $head = Employee::factory()->create(['first_name' => 'Anita', 'last_name' => 'Rao']);

        $this->actingAs(User::factory()->create())
            ->put(route('masters.teams.update', $team), [
                'name' => 'Engineering',
                'code' => $team->code,
                'head_employee_id' => $head->getKey(),
                'is_active' => '1',
            ])
            ->assertRedirect(route('masters.teams.index'));

        $this->assertDatabaseHas('teams', [
            'id' => $team->getKey(),
            'head_employee_id' => $head->getKey(),
        ]);

        $this->actingAs(User::factory()->create())
            ->get(route('masters.teams.index'))
            ->assertOk()
            ->assertSee('Anita Rao');
    }
}
