<?php

declare(strict_types=1);

namespace Tests\Feature;

use Carbon\CarbonImmutable;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Notification;
use Maestro\Core\Database\Seeders\ModuleAndNavigationSeeder;
use Maestro\Core\Database\Seeders\UserSeeder;
use Maestro\Core\Models\MaestroModule;
use Maestro\Core\Modules\Customers\Models\Customer;
use Maestro\Core\Modules\HourRegistration\Database\Seeders\HourRegistrationSeeder;
use Maestro\Core\Modules\HourRegistration\Models\HourInternalCategory;
use Maestro\Core\Modules\HourRegistration\Models\HourTimesheetLine;
use Maestro\Core\Modules\HourRegistration\Models\HourTimesheetWeek;
use Maestro\Core\Modules\HourRegistration\Notifications\HourWeekAwaitingApprovalNotification;
use Spatie\Activitylog\Models\Activity;
use Tests\Fixtures\AuthTestUser;
use Tests\TestCase;

final class HourRegistrationFlowTest extends TestCase
{
    use RefreshDatabase;

    /** @return list<class-string> */
    private function seeds(): array
    {
        return [
            UserSeeder::class,
            ModuleAndNavigationSeeder::class,
            HourRegistrationSeeder::class,
        ];
    }

    private function seedHoursModuleEnabled(): void
    {
        $this->seed($this->seeds());
        MaestroModule::query()->where('slug', 'hours')->update(['is_enabled' => true]);
        Cache::forget('maestro_module:hours');
    }

    public function test_submit_sets_submitted_warnings_and_notifier(): void
    {
        Notification::fake();
        $this->seedHoursModuleEnabled();

        $lead = AuthTestUser::query()->create([
            'name' => 'Lead User',
            'email' => 'lead@example.test',
            'password' => Hash::make('password'),
            'is_active' => true,
            'email_verified_at' => now(),
        ]);
        $lead->assignRole('project_lead');

        $employee = AuthTestUser::query()->create([
            'name' => 'Emp User',
            'email' => 'emp@example.test',
            'password' => Hash::make('password'),
            'is_active' => true,
            'email_verified_at' => now(),
        ]);
        $employee->assignRole('employee');

        $customer = Customer::query()->create([
            'name' => 'ACME BV',
            'is_active' => true,
            'project_manager_id' => $lead->id,
        ]);

        $weekResp = $this->actingAs($employee, 'web')->getJson('/api/hours/week/current');
        $weekResp->assertSuccessful();
        $weekId = (int) $weekResp->json('data.id');

        $linePayload = [[
            'registration_type' => 'customer',
            'customer_id' => $customer->id,
            'duration_mon' => 9,
            'duration_tue' => 0,
            'duration_wed' => 0,
            'duration_thu' => 0,
            'duration_fri' => 0,
            'duration_sat' => 0,
            'duration_sun' => 0,
        ]];

        $this->actingAs($employee, 'web')->putJson("/api/hours/weeks/{$weekId}/lines", [
            'lines' => $linePayload,
            'removed_line_ids' => [],
        ])->assertOk();

        $submit = $this->actingAs($employee, 'web')->postJson("/api/hours/weeks/{$weekId}/submit");
        $submit->assertOk();

        $warnings = $submit->json('warnings');
        self::assertIsArray($warnings);
        self::assertContains('day_hours_exceed_8:mon', $warnings);

        $this->assertDatabaseHas('hour_timesheet_weeks', [
            'id' => $weekId,
            'workflow_status' => 'submitted',
        ]);

        Notification::assertSentTo($lead, HourWeekAwaitingApprovalNotification::class);
    }

    public function test_project_lead_approve_updates_week_and_logs_activity(): void
    {
        Notification::fake();
        $this->seedHoursModuleEnabled();

        $lead = AuthTestUser::query()->create([
            'name' => 'Approver',
            'email' => 'approver@example.test',
            'password' => Hash::make('password'),
            'is_active' => true,
            'email_verified_at' => now(),
        ]);
        $lead->assignRole('project_lead');

        $employee = AuthTestUser::query()->create([
            'name' => 'Emp',
            'email' => 'emp2@example.test',
            'password' => Hash::make('password'),
            'is_active' => true,
            'email_verified_at' => now(),
        ]);
        $employee->assignRole('employee');

        $customer = Customer::query()->create([
            'name' => 'ClientCo',
            'is_active' => true,
            'project_manager_id' => $lead->id,
        ]);

        $wk = HourTimesheetWeek::query()->create([
            'user_id' => $employee->id,
            'week_start' => '2026-05-04',
            'workflow_status' => 'submitted',
            'submitted_at' => now(),
        ]);

        $line = HourTimesheetLine::query()->create([
            'hour_timesheet_week_id' => $wk->id,
            'registration_type' => 'customer',
            'customer_id' => $customer->id,
            'internal_category_id' => null,
            'hour_project_id' => null,
            'hour_task_id' => null,
            'duration_mon' => 2,
            'duration_tue' => 0,
            'duration_wed' => 0,
            'duration_thu' => 0,
            'duration_fri' => 0,
            'duration_sat' => 0,
            'duration_sun' => 0,
            'line_approval_status' => 'pending',
            'approved_by_id' => null,
            'approved_at' => null,
            'reject_reason' => null,
        ]);

        $beforeActivity = Activity::query()->where('subject_type', HourTimesheetLine::class)->count();

        $this->actingAs($lead, 'web')->postJson("/api/hours/lines/{$line->id}/approve")->assertOk();

        self::assertSame(
            'approved',
            HourTimesheetWeek::query()->findOrFail($wk->id)->workflow_status->value,
        );

        self::assertGreaterThan(
            $beforeActivity,
            Activity::query()->where('subject_type', HourTimesheetLine::class)->count(),
        );
    }

    public function test_export_csv_ok_for_filtered_lead_scope(): void
    {
        $this->seedHoursModuleEnabled();

        $lead = AuthTestUser::query()->create([
            'name' => 'LeadExp',
            'email' => 'lead-exp@example.test',
            'password' => Hash::make('password'),
            'is_active' => true,
            'email_verified_at' => now(),
        ]);
        $lead->assignRole('project_lead');

        $employee = AuthTestUser::query()->create([
            'name' => 'EmpExp',
            'email' => 'emp-exp@example.test',
            'password' => Hash::make('password'),
            'is_active' => true,
            'email_verified_at' => now(),
        ]);
        $employee->assignRole('employee');

        $customer = Customer::query()->create([
            'name' => 'ExpCo',
            'is_active' => true,
            'project_manager_id' => $lead->id,
        ]);

        $wk = HourTimesheetWeek::query()->create([
            'user_id' => $employee->id,
            'week_start' => '2026-05-04',
            'workflow_status' => 'approved',
            'submitted_at' => now()->subDay(),
        ]);

        HourTimesheetLine::query()->create([
            'hour_timesheet_week_id' => $wk->id,
            'registration_type' => 'customer',
            'customer_id' => $customer->id,
            'internal_category_id' => null,
            'hour_project_id' => null,
            'hour_task_id' => null,
            'duration_mon' => 1,
            'duration_tue' => 0,
            'duration_wed' => 0,
            'duration_thu' => 0,
            'duration_fri' => 0,
            'duration_sat' => 0,
            'duration_sun' => 0,
            'line_approval_status' => 'approved',
            'approved_by_id' => $lead->id,
            'approved_at' => now(),
            'reject_reason' => null,
        ]);

        $this->actingAs($lead, 'web')
            ->get('/api/hours/export/csv?employee_ids[]='.$employee->id)
            ->assertOk()
            ->assertHeader('content-type', 'text/csv; charset=UTF-8');
    }

    public function test_sync_persists_line_description(): void
    {
        $this->seedHoursModuleEnabled();

        $employee = AuthTestUser::query()->create([
            'name' => 'Emp Desc',
            'email' => 'emp-desc@example.test',
            'password' => Hash::make('password'),
            'is_active' => true,
            'email_verified_at' => now(),
        ]);
        $employee->assignRole('employee');

        $category = HourInternalCategory::query()->firstOrFail();

        $anchor = CarbonImmutable::parse('2026-05-04');
        $this->travelTo((string) $anchor->startOfWeek(CarbonImmutable::MONDAY)->startOfDay()->toDateTimeString());

        $weekResp = $this->actingAs($employee, 'web')->getJson('/api/hours/week/current');
        $weekResp->assertSuccessful();
        $weekId = (int) $weekResp->json('data.id');

        $linePayload = [[
            'registration_type' => 'internal',
            'internal_category_id' => $category->id,
            'description' => '  Memo line  ',
            'duration_mon' => 2,
            'duration_tue' => 0,
            'duration_wed' => 0,
            'duration_thu' => 0,
            'duration_fri' => 0,
            'duration_sat' => 0,
            'duration_sun' => 0,
        ]];

        $saved = $this->actingAs($employee, 'web')->putJson("/api/hours/weeks/{$weekId}/lines", [
            'lines' => $linePayload,
            'removed_line_ids' => [],
        ]);
        $saved->assertOk();
        self::assertSame('Memo line', $saved->json('data.lines.0.description'));
    }

    public function test_workbench_daily_totals_aggregate_across_days_and_weeks(): void
    {
        $this->seedHoursModuleEnabled();

        $employee = AuthTestUser::query()->create([
            'name' => 'Emp Totals',
            'email' => 'emp-totals@example.test',
            'password' => Hash::make('password'),
            'is_active' => true,
            'email_verified_at' => now(),
        ]);
        $employee->assignRole('employee');

        $category = HourInternalCategory::query()->firstOrFail();

        $wk1 = HourTimesheetWeek::query()->create([
            'user_id' => $employee->id,
            'week_start' => '2026-05-04',
            'workflow_status' => 'draft',
            'submitted_at' => null,
        ]);

        HourTimesheetLine::query()->create([
            'hour_timesheet_week_id' => $wk1->id,
            'registration_type' => 'internal',
            'internal_category_id' => $category->id,
            'customer_id' => null,
            'hour_project_id' => null,
            'hour_task_id' => null,
            'duration_mon' => 10,
            'duration_tue' => 0,
            'duration_wed' => 0,
            'duration_thu' => 0,
            'duration_fri' => 0,
            'duration_sat' => 0,
            'duration_sun' => 0,
            'line_approval_status' => 'approved',
            'approved_by_id' => null,
            'approved_at' => now(),
            'reject_reason' => null,
        ]);

        $wk2 = HourTimesheetWeek::query()->create([
            'user_id' => $employee->id,
            'week_start' => '2026-05-11',
            'workflow_status' => 'draft',
            'submitted_at' => null,
        ]);

        HourTimesheetLine::query()->create([
            'hour_timesheet_week_id' => $wk2->id,
            'registration_type' => 'internal',
            'internal_category_id' => $category->id,
            'customer_id' => null,
            'hour_project_id' => null,
            'hour_task_id' => null,
            'duration_mon' => 5,
            'duration_tue' => 0,
            'duration_wed' => 0,
            'duration_thu' => 0,
            'duration_fri' => 0,
            'duration_sat' => 0,
            'duration_sun' => 0,
            'line_approval_status' => 'approved',
            'approved_by_id' => null,
            'approved_at' => now(),
            'reject_reason' => null,
        ]);

        $resp = $this->actingAs($employee, 'web')->getJson(
            '/api/hours/workbench/daily-totals?from=2026-05-04&to=2026-05-11',
        );
        $resp->assertOk();
        $byDate = collect($resp->json('data'))->keyBy('date');
        self::assertEquals(10.0, $byDate['2026-05-04']['hours']);
        self::assertEquals(5.0, $byDate['2026-05-11']['hours']);
        self::assertEquals(0.0, $byDate['2026-05-05']['hours']);
    }
}
