<?php

namespace Tests\Feature;

use App\Models\Allocation;
use App\Models\Project;
use App\Models\ProjectMonthPlan;
use App\Models\Role;
use App\Models\TeamMember;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class ReportTest extends TestCase
{
    use RefreshDatabase;

    protected function loginAsManager(): string
    {
        $user = User::factory()->create([
            'email' => 'manager@test.com',
            'password' => bcrypt('password123'),
            'role' => 'manager',
            'active' => true,
        ]);

        $response = $this->postJson('/api/auth/login', [
            'email' => 'manager@test.com',
            'password' => 'password123',
        ]);

        return $response->json('access_token');
    }

    // Task 4.1: Reporting payload includes lifecycle total, month plan, month execution, and variances
    public function test_reporting_payload_includes_all_required_data(): void
    {
        $token = $this->loginAsManager();

        // Create project with approved estimate
        $project = Project::factory()->create([
            'approved_estimate' => 3000,
        ]);

        // Create month plans
        ProjectMonthPlan::create([
            'project_id' => $project->id,
            'month' => '2026-01-01',
            'planned_hours' => 1200,
        ]);
        ProjectMonthPlan::create([
            'project_id' => $project->id,
            'month' => '2026-02-01',
            'planned_hours' => 1400,
        ]);

        // Create team member
        $role = Role::factory()->create();
        $member = TeamMember::factory()->create(['role_id' => $role->id, 'active' => true]);

        // Create allocation
        Allocation::factory()->create([
            'project_id' => $project->id,
            'team_member_id' => $member->id,
            'month' => '2026-01-01',
            'allocated_hours' => 1300,
        ]);

        $response = $this->withHeader('Authorization', "Bearer {$token}")
            ->getJson('/api/reports/allocations?start_date=2026-01-01&end_date=2026-02-28');

        $response->assertStatus(200);

        // Verify top-level structure
        $response->assertJsonStructure([
            'period' => ['start', 'end'],
            'view_type',
            'projects' => [
                '*' => [
                    'id',
                    'code',
                    'title',
                    'approved_estimate',
                    'lifecycle_status',
                    'plan_sum',
                    'period_planned',
                    'period_allocated',
                    'period_variance',
                    'period_status',
                    'months' => [
                        '*' => [
                            'month',
                            'planned_hours',
                            'is_blank',
                            'allocated_hours',
                            'variance',
                            'status',
                        ],
                    ],
                ],
            ],
            'members' => [
                '*' => [
                    'id',
                    'name',
                    'period_allocated',
                    'projects' => [
                        '*' => [
                            'project_id',
                            'project_code',
                            'project_title',
                            'total_hours',
                        ],
                    ],
                ],
            ],
            'aggregates' => [
                'total_planned',
                'total_allocated',
                'total_variance',
                'status',
            ],
        ]);

        // Verify data values
        $data = $response->json();
        $projectData = collect($data['projects'])->firstWhere('id', $project->id);

        $this->assertEquals(3000.0, $projectData['approved_estimate']);
        $this->assertEquals(2600.0, $projectData['plan_sum']); // 1200 + 1400
        $this->assertEquals(2600.0, $projectData['period_planned']);
        $this->assertEquals(1300.0, $projectData['period_allocated']);
        $this->assertEquals(-1300.0, $projectData['period_variance']);
        $this->assertEquals('UNDER', $projectData['period_status']);
        $this->assertEquals('UNDER', $projectData['lifecycle_status']);
    }

    // Task 4.2: Historical/current/future month slices are consistent
    public function test_view_type_did_for_past_months(): void
    {
        $token = $this->loginAsManager();
        Project::factory()->create();

        $now = Carbon::now();
        $pastStart = $now->copy()->subMonths(3)->startOfMonth();
        $pastEnd = $now->copy()->subMonth()->endOfMonth();

        $response = $this->withHeader('Authorization', "Bearer {$token}")
            ->getJson("/api/reports/allocations?start_date={$pastStart->format('Y-m-d')}&end_date={$pastEnd->format('Y-m-d')}");

        $response->assertStatus(200);
        $response->assertJsonPath('view_type', 'did');
    }

    public function test_view_type_is_for_current_month(): void
    {
        $token = $this->loginAsManager();
        Project::factory()->create();

        $now = Carbon::now();
        $currentStart = $now->copy()->startOfMonth();
        $currentEnd = $now->copy()->endOfMonth();

        $response = $this->withHeader('Authorization', "Bearer {$token}")
            ->getJson("/api/reports/allocations?start_date={$currentStart->format('Y-m-d')}&end_date={$currentEnd->format('Y-m-d')}");

        $response->assertStatus(200);
        $response->assertJsonPath('view_type', 'is');
    }

    public function test_view_type_will_for_future_months(): void
    {
        $token = $this->loginAsManager();
        Project::factory()->create();

        $now = Carbon::now();
        $futureStart = $now->copy()->addMonth()->startOfMonth();
        $futureEnd = $now->copy()->addMonths(3)->endOfMonth();

        $response = $this->withHeader('Authorization', "Bearer {$token}")
            ->getJson("/api/reports/allocations?start_date={$futureStart->format('Y-m-d')}&end_date={$futureEnd->format('Y-m-d')}");

        $response->assertStatus(200);
        $response->assertJsonPath('view_type', 'will');
    }

    // Task 4.4: Distinguish blank plan vs explicit zero
    public function test_distinguishes_blank_plan_from_explicit_zero(): void
    {
        $token = $this->loginAsManager();

        $project = Project::factory()->create();
        $role = Role::factory()->create();
        $member = TeamMember::factory()->create(['role_id' => $role->id, 'active' => true]);

        // January: explicit plan of 100
        ProjectMonthPlan::create([
            'project_id' => $project->id,
            'month' => '2026-01-01',
            'planned_hours' => 100,
        ]);

        // February: explicit plan of 0
        ProjectMonthPlan::create([
            'project_id' => $project->id,
            'month' => '2026-02-01',
            'planned_hours' => 0,
        ]);

        // March: blank (no plan entry)

        Allocation::factory()->create([
            'project_id' => $project->id,
            'team_member_id' => $member->id,
            'month' => '2026-01-01',
            'allocated_hours' => 80,
        ]);
        Allocation::factory()->create([
            'project_id' => $project->id,
            'team_member_id' => $member->id,
            'month' => '2026-02-01',
            'allocated_hours' => 50,
        ]);
        Allocation::factory()->create([
            'project_id' => $project->id,
            'team_member_id' => $member->id,
            'month' => '2026-03-01',
            'allocated_hours' => 30,
        ]);

        $response = $this->withHeader('Authorization', "Bearer {$token}")
            ->getJson('/api/reports/allocations?start_date=2026-01-01&end_date=2026-03-31');

        $response->assertStatus(200);
        $data = $response->json();
        $projectData = collect($data['projects'])->firstWhere('id', $project->id);

        $january = collect($projectData['months'])->firstWhere('month', '2026-01');
        $february = collect($projectData['months'])->firstWhere('month', '2026-02');
        $march = collect($projectData['months'])->firstWhere('month', '2026-03');

        // January: explicit 100
        $this->assertEquals(100.0, $january['planned_hours']);
        $this->assertFalse($january['is_blank']);

        // February: explicit 0
        $this->assertEquals(0.0, $february['planned_hours']);
        $this->assertFalse($february['is_blank']);

        // March: blank
        $this->assertNull($march['planned_hours']);
        $this->assertTrue($march['is_blank']);
    }

    public function test_filters_by_project_ids(): void
    {
        $token = $this->loginAsManager();

        $project1 = Project::factory()->create();
        $project2 = Project::factory()->create();
        $role = Role::factory()->create();
        $member = TeamMember::factory()->create(['role_id' => $role->id, 'active' => true]);

        ProjectMonthPlan::create([
            'project_id' => $project1->id,
            'month' => '2026-01-01',
            'planned_hours' => 100,
        ]);
        ProjectMonthPlan::create([
            'project_id' => $project2->id,
            'month' => '2026-01-01',
            'planned_hours' => 200,
        ]);

        Allocation::factory()->create([
            'project_id' => $project1->id,
            'team_member_id' => $member->id,
            'month' => '2026-01-01',
            'allocated_hours' => 80,
        ]);
        Allocation::factory()->create([
            'project_id' => $project2->id,
            'team_member_id' => $member->id,
            'month' => '2026-01-01',
            'allocated_hours' => 150,
        ]);

        $response = $this->withHeader('Authorization', "Bearer {$token}")
            ->getJson("/api/reports/allocations?start_date=2026-01-01&end_date=2026-01-31&project_ids[]={$project1->id}");

        $response->assertStatus(200);
        $data = $response->json();

        $this->assertCount(1, $data['projects']);
        $this->assertEquals($project1->id, $data['projects'][0]['id']);
        $this->assertEquals(100.0, $data['aggregates']['total_planned']);
    }

    public function test_includes_untracked_allocations(): void
    {
        $token = $this->loginAsManager();

        $project = Project::factory()->create();
        $role = Role::factory()->create();
        $member = TeamMember::factory()->create(['role_id' => $role->id, 'active' => true]);

        ProjectMonthPlan::create([
            'project_id' => $project->id,
            'month' => '2026-01-01',
            'planned_hours' => 200,
        ]);

        // Tracked allocation
        Allocation::factory()->create([
            'project_id' => $project->id,
            'team_member_id' => $member->id,
            'month' => '2026-01-01',
            'allocated_hours' => 100,
        ]);

        // Untracked allocation
        Allocation::factory()->create([
            'project_id' => $project->id,
            'team_member_id' => null,
            'month' => '2026-01-01',
            'allocated_hours' => 50,
        ]);

        $response = $this->withHeader('Authorization', "Bearer {$token}")
            ->getJson('/api/reports/allocations?start_date=2026-01-01&end_date=2026-01-31');

        $response->assertStatus(200);
        $data = $response->json();

        // Project should have 150 total (100 tracked + 50 untracked)
        $projectData = collect($data['projects'])->firstWhere('id', $project->id);
        $this->assertEquals(150.0, $projectData['period_allocated']);

        // Members should include untracked row
        $untrackedRow = collect($data['members'])->firstWhere('name', 'Untracked');
        $this->assertNotNull($untrackedRow);
        $this->assertEquals(50.0, $untrackedRow['period_allocated']);
    }

    public function test_validates_required_date_parameters(): void
    {
        $token = $this->loginAsManager();

        $response = $this->withHeader('Authorization', "Bearer {$token}")
            ->getJson('/api/reports/allocations');

        $response->assertStatus(422);
        $response->assertJsonValidationErrors(['start_date', 'end_date']);
    }

    public function test_validates_end_date_after_start_date(): void
    {
        $token = $this->loginAsManager();

        $response = $this->withHeader('Authorization', "Bearer {$token}")
            ->getJson('/api/reports/allocations?start_date=2026-03-01&end_date=2026-01-01');

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