<?php

namespace Tests\Feature\ExpenseManagement;

use App\Enums\Status;
use App\Models\ExpenseType;
use App\Models\Settings;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Spatie\Permission\Models\Role;
use Tests\TestCase;
use Tests\Traits\CreatesUsers;

class ExpenseTypeControllerTest extends TestCase
{
    use CreatesUsers, RefreshDatabase;

    protected function setUp(): void
    {
        parent::setUp();
        Role::create(['name' => 'admin', 'guard_name' => 'web', 'is_web_access_enabled' => true]);
        Role::create(['name' => 'hr', 'guard_name' => 'web', 'is_web_access_enabled' => true]);
        Role::create(['name' => 'field_employee', 'guard_name' => 'web']);

        // Create settings record required for views
        Settings::create([
            'currency_symbol' => '$',
            'app_name' => 'Test App',
        ]);
    }

    public function test_guest_cannot_access_expense_types_index(): void
    {
        $response = $this->get(route('hrcore.expense-types.index'));

        $response->assertRedirect('/auth/login');
    }

    public function test_admin_can_view_expense_types_index(): void
    {
        $this->actingAsAdmin();

        $response = $this->get(route('hrcore.expense-types.index'));

        $response->assertOk();
        $response->assertViewIs('expense-types.index');
    }

    public function test_datatable_returns_expense_types(): void
    {
        $this->actingAsAdmin();

        // Create some expense types
        ExpenseType::factory()->count(5)->create();

        $response = $this->getJson(route('hrcore.expense-types.datatable', [
            'draw' => 1,
            'start' => 0,
            'length' => 10,
        ]));

        $response->assertOk();
        $response->assertJsonStructure([
            'draw',
            'recordsTotal',
            'recordsFiltered',
            'data',
        ]);

        $this->assertEquals(5, $response->json('recordsTotal'));
    }

    public function test_datatable_search_filters_results(): void
    {
        $this->actingAsAdmin();

        // Create expense types with unique names that won't appear randomly in faker
        ExpenseType::factory()->create(['name' => 'UniqueSearchTestType']);
        ExpenseType::factory()->create(['name' => 'AnotherDifferentType']);
        ExpenseType::factory()->create(['name' => 'ThirdDistinctType']);

        $response = $this->getJson(route('hrcore.expense-types.datatable', [
            'draw' => 1,
            'start' => 0,
            'length' => 10,
            'search' => ['value' => 'UniqueSearchTestType'],
        ]));

        $response->assertOk();
        $this->assertEquals(1, $response->json('recordsFiltered'));
    }

    public function test_can_create_expense_type_with_valid_data(): void
    {
        $this->actingAsAdmin();

        $data = [
            'name' => 'New Expense Type',
            'description' => 'Test description',
            'status' => Status::ACTIVE->value,
        ];

        $response = $this->postJson(route('hrcore.expense-types.store'), $data);

        $response->assertOk();
        $response->assertJson(['status' => 'success']);

        $this->assertDatabaseHas('expense_types', [
            'name' => 'New Expense Type',
            'notes' => 'Test description',
        ]);
    }

    public function test_cannot_create_expense_type_without_name(): void
    {
        $this->actingAsAdmin();

        $data = [
            'description' => 'Test description',
            'status' => Status::ACTIVE->value,
        ];

        $response = $this->postJson(route('hrcore.expense-types.store'), $data);

        $response->assertStatus(422);
        $response->assertJsonValidationErrors(['name']);
    }

    public function test_cannot_create_expense_type_without_status(): void
    {
        $this->actingAsAdmin();

        $data = [
            'name' => 'New Expense Type',
            'description' => 'Test description',
        ];

        $response = $this->postJson(route('hrcore.expense-types.store'), $data);

        $response->assertStatus(422);
        $response->assertJsonValidationErrors(['status']);
    }

    public function test_can_update_expense_type(): void
    {
        $this->actingAsAdmin();

        $expenseType = ExpenseType::factory()->create();

        $data = [
            'name' => 'Updated Expense Type',
            'description' => 'Updated description',
            'status' => Status::INACTIVE->value,
        ];

        $response = $this->putJson(route('hrcore.expense-types.update', $expenseType->id), $data);

        $response->assertOk();
        $response->assertJson(['status' => 'success']);

        $this->assertDatabaseHas('expense_types', [
            'id' => $expenseType->id,
            'name' => 'Updated Expense Type',
            'notes' => 'Updated description',
        ]);
    }

    public function test_can_delete_expense_type(): void
    {
        $this->actingAsAdmin();

        $expenseType = ExpenseType::factory()->create();

        $response = $this->deleteJson(route('hrcore.expense-types.destroy', $expenseType->id));

        $response->assertOk();
        $response->assertJson(['status' => 'success']);
    }

    public function test_deleted_expense_type_is_soft_deleted(): void
    {
        $this->actingAsAdmin();

        $expenseType = ExpenseType::factory()->create();

        $this->deleteJson(route('hrcore.expense-types.destroy', $expenseType->id));

        $this->assertSoftDeleted('expense_types', [
            'id' => $expenseType->id,
        ]);
    }

    public function test_datatable_supports_pagination(): void
    {
        $this->actingAsAdmin();

        // Create 15 expense types
        ExpenseType::factory()->count(15)->create();

        $response = $this->getJson(route('hrcore.expense-types.datatable', [
            'draw' => 1,
            'start' => 0,
            'length' => 5,
        ]));

        $response->assertOk();
        $this->assertEquals(15, $response->json('recordsTotal'));
        $this->assertCount(5, $response->json('data'));
    }
}
