<?php

namespace Tests\Feature\ExpenseManagement;

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

class ExpenseReportControllerTest 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 for currency symbol
        Settings::create([
            'currency_symbol' => '$',
            'app_name' => 'Test App',
        ]);
    }

    public function test_guest_cannot_access_expense_summary(): void
    {
        $response = $this->get(route('expenses.reports.summary'));

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

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

        $response = $this->get(route('expenses.reports.summary'));

        $response->assertOk();
        $response->assertViewIs('expenses.reports.expense-summary');
    }

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

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

        // Create some expenses
        ExpenseRequest::factory()
            ->count(5)
            ->forUser($employee)
            ->forExpenseType($expenseType)
            ->pending()
            ->withAmount(100)
            ->create(['for_date' => now()]);

        ExpenseRequest::factory()
            ->count(3)
            ->forUser($employee)
            ->forExpenseType($expenseType)
            ->approved()
            ->create([
                'for_date' => now(),
                'amount' => 150,
                'approved_amount' => 150,
            ]);

        $response = $this->getJson(route('expenses.reports.summary.data', [
            'date_from' => now()->startOfMonth()->format('Y-m-d'),
            'date_to' => now()->endOfMonth()->format('Y-m-d'),
        ]));

        $response->assertOk();
        $response->assertJson(['success' => true]);
        $response->assertJsonStructure([
            'success',
            'statistics' => [
                'total_submitted',
                'total_approved',
                'approval_rate',
                'total_requests',
            ],
            'chartData',
        ]);
    }

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

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

        // Create expenses for this month
        ExpenseRequest::factory()
            ->count(3)
            ->forUser($employee)
            ->forExpenseType($expenseType)
            ->create(['for_date' => now()]);

        // Create expenses for last month
        ExpenseRequest::factory()
            ->count(5)
            ->forUser($employee)
            ->forExpenseType($expenseType)
            ->create(['for_date' => now()->subMonth()]);

        $response = $this->getJson(route('expenses.reports.summary.data', [
            'date_from' => now()->startOfMonth()->format('Y-m-d'),
            'date_to' => now()->endOfMonth()->format('Y-m-d'),
        ]));

        $response->assertOk();
        $this->assertEquals(3, $response->json('statistics.total_requests'));
    }

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

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

        // Create approved expenses
        ExpenseRequest::factory()
            ->count(3)
            ->forUser($employee)
            ->forExpenseType($expenseType)
            ->approved()
            ->create([
                'for_date' => now(),
                'amount' => 100,
                'approved_amount' => 100,
            ]);

        // Create pending expenses
        ExpenseRequest::factory()
            ->count(5)
            ->forUser($employee)
            ->forExpenseType($expenseType)
            ->pending()
            ->create(['for_date' => now()]);

        $response = $this->getJson(route('expenses.reports.summary.data', [
            'date_from' => now()->startOfMonth()->format('Y-m-d'),
            'date_to' => now()->endOfMonth()->format('Y-m-d'),
            'status' => 'approved',
        ]));

        $response->assertOk();
        $this->assertEquals(3, $response->json('statistics.total_requests'));
    }

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

        $expenseType1 = ExpenseType::factory()->create();
        $expenseType2 = ExpenseType::factory()->create();
        $employee = User::factory()->create();

        // Create expenses for type 1
        ExpenseRequest::factory()
            ->count(3)
            ->forUser($employee)
            ->forExpenseType($expenseType1)
            ->create(['for_date' => now()]);

        // Create expenses for type 2
        ExpenseRequest::factory()
            ->count(5)
            ->forUser($employee)
            ->forExpenseType($expenseType2)
            ->create(['for_date' => now()]);

        $response = $this->getJson(route('expenses.reports.summary.data', [
            'date_from' => now()->startOfMonth()->format('Y-m-d'),
            'date_to' => now()->endOfMonth()->format('Y-m-d'),
            'expense_type_id' => $expenseType1->id,
        ]));

        $response->assertOk();
        $this->assertEquals(3, $response->json('statistics.total_requests'));
    }

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

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

        ExpenseRequest::factory()
            ->count(5)
            ->forUser($employee)
            ->forExpenseType($expenseType)
            ->create(['for_date' => now()]);

        $response = $this->getJson(route('expenses.reports.summary.table', [
            'draw' => 1,
            'start' => 0,
            'length' => 10,
            'date_from' => now()->startOfMonth()->format('Y-m-d'),
            'date_to' => now()->endOfMonth()->format('Y-m-d'),
        ]));

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

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

        ExpenseType::factory()->create();

        $response = $this->get(route('expenses.employee-report'));

        $response->assertOk();
        $response->assertViewIs('expenses.reports.employee-expense');
    }

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

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

        // Create some expenses
        ExpenseRequest::factory()
            ->count(5)
            ->forUser($employee)
            ->forExpenseType($expenseType)
            ->approved()
            ->create([
                'for_date' => now(),
                'amount' => 100,
                'approved_amount' => 100,
            ]);

        $response = $this->getJson(route('expenses.employee-report.statistics', [
            'date_from' => now()->startOfMonth()->format('Y-m-d'),
            'date_to' => now()->endOfMonth()->format('Y-m-d'),
        ]));

        $response->assertOk();
        $response->assertJson(['success' => true]);
        $response->assertJsonStructure([
            'success',
            'data' => [
                'total_employees',
                'total_submitted',
                'avg_per_employee',
                'total_pending_approvals',
                'compliance_rate',
                'top_employees',
                'monthly_trend',
            ],
        ]);
    }

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

        $response = $this->get(route('expenses.reports.approval-pipeline'));

        $response->assertOk();
        $response->assertViewIs('expenses.reports.approval-pipeline');
    }

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

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

        // Create expenses
        ExpenseRequest::factory()
            ->count(5)
            ->forUser($employee)
            ->forExpenseType($expenseType)
            ->pending()
            ->create(['for_date' => now()]);

        ExpenseRequest::factory()
            ->count(3)
            ->forUser($employee)
            ->forExpenseType($expenseType)
            ->approved()
            ->create([
                'for_date' => now(),
                'approved_at' => now(),
            ]);

        $response = $this->getJson(route('expenses.reports.approval-pipeline.statistics', [
            'date_from' => now()->subMonth()->format('Y-m-d'),
            'date_to' => now()->format('Y-m-d'),
        ]));

        $response->assertOk();
        $response->assertJson(['status' => 'success']);
        $response->assertJsonStructure([
            'status',
            'data' => [
                'total_pending',
                'total_pending_amount',
                'avg_days_pending',
                'over_7_days',
                'approval_rate',
                'avg_approval_time_hours',
                'status_distribution',
                'pending_by_approver',
            ],
        ]);
    }

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

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

        // Create expenses for employee 1
        ExpenseRequest::factory()
            ->count(3)
            ->forUser($employee1)
            ->forExpenseType($expenseType)
            ->create(['for_date' => now()]);

        // Create expenses for employee 2
        ExpenseRequest::factory()
            ->count(5)
            ->forUser($employee2)
            ->forExpenseType($expenseType)
            ->create(['for_date' => now()]);

        $response = $this->getJson(route('expenses.employee-report.statistics', [
            'date_from' => now()->startOfMonth()->format('Y-m-d'),
            'date_to' => now()->endOfMonth()->format('Y-m-d'),
            'employee_id' => $employee1->id,
        ]));

        $response->assertOk();
        $this->assertEquals(1, $response->json('data.total_employees'));
    }

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

        $expenseType1 = ExpenseType::factory()->create(['name' => 'Travel']);
        $expenseType2 = ExpenseType::factory()->create(['name' => 'Food']);
        $employee = User::factory()->create();

        ExpenseRequest::factory()
            ->count(2)
            ->forUser($employee)
            ->forExpenseType($expenseType1)
            ->approved()
            ->create([
                'for_date' => now(),
                'amount' => 100,
                'approved_amount' => 100,
            ]);

        ExpenseRequest::factory()
            ->count(3)
            ->forUser($employee)
            ->forExpenseType($expenseType2)
            ->approved()
            ->create([
                'for_date' => now(),
                'amount' => 150,
                'approved_amount' => 150,
            ]);

        $response = $this->getJson(route('expenses.reports.summary.data', [
            'date_from' => now()->startOfMonth()->format('Y-m-d'),
            'date_to' => now()->endOfMonth()->format('Y-m-d'),
        ]));

        $response->assertOk();
        $response->assertJsonStructure([
            'chartData' => [
                'donut' => [
                    'labels',
                    'series',
                ],
                'bar' => [
                    'categories',
                    'submitted',
                    'approved',
                ],
            ],
        ]);
    }

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

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

        // Create expenses with documents (compliant)
        ExpenseRequest::factory()
            ->count(3)
            ->forUser($employee)
            ->forExpenseType($expenseType)
            ->withDocument()
            ->create(['for_date' => now()]);

        // Create expenses without documents (non-compliant)
        ExpenseRequest::factory()
            ->count(2)
            ->forUser($employee)
            ->forExpenseType($expenseType)
            ->create([
                'for_date' => now(),
                'document_url' => null,
            ]);

        $response = $this->getJson(route('expenses.employee-report.statistics', [
            'date_from' => now()->startOfMonth()->format('Y-m-d'),
            'date_to' => now()->endOfMonth()->format('Y-m-d'),
        ]));

        $response->assertOk();
        // 3 out of 5 have documents = 60%
        $this->assertEquals(60.0, $response->json('data.compliance_rate'));
    }
}
