<?php

namespace Tests\Feature;

use App\Models\Company;
use App\Models\Designation;
use App\Models\Location;
use App\Models\Role;
use App\Models\Shift;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Tests\TestCase;

class EmployeeNumberGenerationTest extends TestCase
{
    use RefreshDatabase;

    public function test_it_auto_generates_employee_number_using_company_prefix(): void
    {
        $this->withoutMiddleware();
        [$actor, $role, $work] = $this->createActor();

        DB::table('employee_number_sequences')->insert([
            'company_id' => $actor->company_id,
            'prefix' => 'ACM',
            'digits' => 3,
            'last_number' => 10,
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $email = 'new-' . Str::uuid() . '@demo.local';
        $response = $this->actingAs($actor)->post(route('employees.store'), array_merge($work, [
            'name' => 'Generated Employee',
            'email' => $email,
            'password' => 'password123',
            'employee_number' => '',
            'role_id' => $role->id,
        ]));

        $response->assertSessionHasNoErrors();
        $response->assertRedirect();
        $this->assertDatabaseHas('users', [
            'email' => $email,
            'employee_number' => 'ACM011',
        ]);
    }

    public function test_it_uses_per_company_sequence_only(): void
    {
        $this->withoutMiddleware();
        [$actor, $role, $work] = $this->createActor('alpha-admin@demo.local', 'Alpha Co');

        $otherCompany = Company::query()->create([
            'name' => 'Beta Co',
            'short_name' => 'BETA',
            'email' => 'beta@demo.local',
            'status' => 'active',
        ]);

        DB::table('employee_number_sequences')->insert([
            'company_id' => $actor->company_id,
            'prefix' => 'EMP',
            'digits' => 5,
            'last_number' => 2,
            'created_at' => now(),
            'updated_at' => now(),
        ]);
        DB::table('employee_number_sequences')->insert([
            'company_id' => $otherCompany->id,
            'prefix' => 'EMP',
            'digits' => 5,
            'last_number' => 40,
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $email = 'alpha-new-' . Str::uuid() . '@demo.local';
        $this->actingAs($actor)->post(route('employees.store'), array_merge($work, [
            'name' => 'Alpha Employee',
            'email' => $email,
            'password' => 'password123',
            'employee_number' => '',
            'role_id' => $role->id,
        ]))->assertSessionHasNoErrors();

        $this->assertDatabaseHas('users', [
            'email' => $email,
            'company_id' => $actor->company_id,
            'employee_number' => 'EMP00003',
        ]);
    }

    public function test_it_preserves_manual_employee_number_and_validates_uniqueness(): void
    {
        $this->withoutMiddleware();
        [$actor, $role, $work] = $this->createActor();

        $manualEmployeeNumber = 'MANUAL-77';
        $this->actingAs($actor)->post(route('employees.store'), array_merge($work, [
            'name' => 'Manual Employee',
            'email' => 'manual-' . Str::uuid() . '@demo.local',
            'password' => 'password123',
            'employee_number' => '  ' . $manualEmployeeNumber . '  ',
            'role_id' => $role->id,
        ]))->assertSessionHasNoErrors();

        $this->assertDatabaseHas('users', [
            'company_id' => $actor->company_id,
            'employee_number' => $manualEmployeeNumber,
        ]);

        $this->actingAs($actor)->post(route('employees.store'), array_merge($work, [
            'name' => 'Duplicate Manual Employee',
            'email' => 'manual-dup-' . Str::uuid() . '@demo.local',
            'password' => 'password123',
            'employee_number' => $manualEmployeeNumber,
            'role_id' => $role->id,
        ]))->assertSessionHasErrors(['employee_number']);
    }

    public function test_it_falls_back_to_emp_prefix_when_setting_missing(): void
    {
        $this->withoutMiddleware();
        [$actor, $role, $work] = $this->createActor();

        DB::table('employee_number_sequences')->insert([
            'company_id' => $actor->company_id,
            'prefix' => 'EMP',
            'digits' => 5,
            'last_number' => 7,
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $email = 'fallback-' . Str::uuid() . '@demo.local';
        $this->actingAs($actor)->post(route('employees.store'), array_merge($work, [
            'name' => 'Fallback Prefix Employee',
            'email' => $email,
            'password' => 'password123',
            'employee_number' => '',
            'role_id' => $role->id,
        ]))->assertSessionHasNoErrors();

        $this->assertDatabaseHas('users', [
            'email' => $email,
            'employee_number' => 'EMP00008',
        ]);
    }

    /**
     * @return array{0: User, 1: Role, 2: array<string, int>}
     */
    private function createActor(
        string $email = 'employee-admin@demo.local',
        string $companyName = 'Demo Company'
    ): array {
        $company = Company::query()->create([
            'name' => $companyName,
            'short_name' => strtoupper(substr($companyName, 0, 4)),
            'email' => $email,
            'status' => 'active',
        ]);

        $role = Role::withoutGlobalScopes()->create([
            'company_id' => $company->id,
            'name' => 'admin',
            'display_name' => 'Admin',
        ]);

        $user = User::factory()->create([
            'company_id' => $company->id,
            'role_id' => $role->id,
            'email' => $email,
            'is_superadmin' => false,
            'user_type' => \App\Models\User::USER_TYPE_ADMIN,
            'status' => 'active',
        ]);

        $designation = Designation::withoutGlobalScopes()->create([
            'company_id' => $company->id,
            'name' => 'Staff',
        ]);
        $location = Location::withoutGlobalScopes()->create([
            'company_id' => $company->id,
            'name' => 'Office',
        ]);
        $shift = Shift::withoutGlobalScopes()->create([
            'company_id' => $company->id,
            'name' => 'Day',
            'clock_in_time' => '09:00',
            'clock_out_time' => '18:00',
            'is_active' => true,
        ]);

        return [$user, $role, [
            'designation_id' => $designation->id,
            'location_id' => $location->id,
            'shift_id' => $shift->id,
            'report_to' => $user->id,
        ]];
    }
}
