<?php

use App\Models\Holiday;
use App\Models\Pto;
use App\Models\Role;
use App\Models\TeamMember;
use App\Models\TeamMemberAvailability;
use App\Models\User;
use App\Services\CapacityService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

use function Pest\Laravel\assertDatabaseHas;

/**
 * @mixin \Tests\TestCase
 */
uses(TestCase::class, RefreshDatabase::class);

test('4.1.11 GET /api/capacity calculates individual capacity', function () {
    $token = loginAsManager($this);
    $role = Role::factory()->create();
    $teamMember = TeamMember::factory()->create(['role_id' => $role->id]);

    $response = $this->getJson("/api/capacity?month=2026-02&team_member_id={$teamMember->id}", [
        'Authorization' => "Bearer {$token}",
    ]);

    $response->assertStatus(200);
    $service = app(CapacityService::class);
    $capacity = $service->calculateIndividualCapacity($teamMember->id, '2026-02');
    $expected = [
        'data' => [
            'team_member_id' => $teamMember->id,
            'month' => '2026-02',
            'working_days' => $service->calculateWorkingDays('2026-02'),
            'person_days' => $capacity['person_days'],
            'hours' => $capacity['hours'],
            'details' => $capacity['details'],
        ],
    ];

    $response->assertExactJson($expected);
});

test('4.1.12 Capacity accounts for availability', function () {
    $token = loginAsManager($this);
    $role = Role::factory()->create();
    $member = TeamMember::factory()->create(['role_id' => $role->id]);

    TeamMemberAvailability::factory()->forDate('2026-02-03')->availability(0.5)->create(['team_member_id' => $member->id]);
    TeamMemberAvailability::factory()->forDate('2026-02-04')->availability(0.0)->create(['team_member_id' => $member->id]);

    $response = $this->getJson("/api/capacity?month=2026-02&team_member_id={$member->id}", [
        'Authorization' => "Bearer {$token}",
    ]);

    $response->assertStatus(200);
    $details = collect($response->json('data.details'));

    expect($details->firstWhere('date', '2026-02-03')['availability'])->toBe(0.5);
    expect($details->firstWhere('date', '2026-02-04')['availability'])->toBe(0);
});

test('4.1.13 Capacity subtracts PTO', function () {
    $token = loginAsManager($this);
    $role = Role::factory()->create();
    $member = TeamMember::factory()->create(['role_id' => $role->id]);

    Pto::create([
        'team_member_id' => $member->id,
        'start_date' => '2026-02-10',
        'end_date' => '2026-02-12',
        'reason' => 'Vacation',
        'status' => 'approved',
    ]);

    $response = $this->getJson("/api/capacity?month=2026-02&team_member_id={$member->id}", [
        'Authorization' => "Bearer {$token}",
    ]);

    $response->assertStatus(200);
    $details = collect($response->json('data.details'));

    expect($details->where('is_pto', true)->count())->toBe(3);
    expect($details->firstWhere('date', '2026-02-11')['availability'])->toBe(0);
});

test('4.1.14 Capacity subtracts holidays', function () {
    $token = loginAsManager($this);
    $role = Role::factory()->create();
    $member = TeamMember::factory()->create(['role_id' => $role->id]);

    Holiday::create([
        'date' => '2026-02-17',
        'name' => 'Presidents Day',
        'description' => 'Company wide',
    ]);

    $response = $this->getJson("/api/capacity?month=2026-02&team_member_id={$member->id}", [
        'Authorization' => "Bearer {$token}",
    ]);

    $response->assertStatus(200);
    $dates = collect($response->json('details'))->pluck('date');

    expect($dates)->not->toContain('2026-02-17');
});

test('4.1.15 GET /api/capacity/team sums active members', function () {
    $token = loginAsManager($this);
    $role = Role::factory()->create();
    $activeA = TeamMember::factory()->create(['role_id' => $role->id]);
    $activeB = TeamMember::factory()->create(['role_id' => $role->id]);
    TeamMember::factory()->inactive()->create(['role_id' => $role->id]);

    $expectedDays = 0;
    $expectedHours = 0;

    foreach ([$activeA, $activeB] as $member) {
        $capacity = app(CapacityService::class)->calculateIndividualCapacity($member->id, '2026-02');
        $expectedDays += $capacity['person_days'];
        $expectedHours += $capacity['hours'];
    }

    $response = $this->getJson('/api/capacity/team?month=2026-02', [
        'Authorization' => "Bearer {$token}",
    ]);

    $response->assertStatus(200);
    $response->assertJsonCount(2, 'data.members');
    expect(round($response->json('data.person_days'), 2))->toBe(round($expectedDays, 2));
    expect($response->json('data.hours'))->toBe($expectedHours);
});

test('4.1.16 GET /api/capacity/revenue calculates possible revenue', function () {
    $token = loginAsManager($this);
    $role = Role::factory()->create();
    TeamMember::factory()->create(['role_id' => $role->id, 'hourly_rate' => 150]);
    TeamMember::factory()->create(['role_id' => $role->id, 'hourly_rate' => 125]);

    $expectedRevenue = app(CapacityService::class)->calculatePossibleRevenue('2026-02');

    $response = $this->getJson('/api/capacity/revenue?month=2026-02', [
        'Authorization' => "Bearer {$token}",
    ]);

    $response->assertStatus(200);
    expect(round($response->json('data.possible_revenue'), 2))->toBe(round($expectedRevenue, 2));
});

test('4.1.25 POST /api/capacity/availability saves entry', function () {
    $token = loginAsManager($this);
    $role = Role::factory()->create();
    $member = TeamMember::factory()->create(['role_id' => $role->id]);

    $payload = [
        'team_member_id' => $member->id,
        'date' => '2026-02-03',
        'availability' => 0.5,
    ];

    $response = $this->postJson('/api/capacity/availability', $payload, [
        'Authorization' => "Bearer {$token}",
    ]);

    $response->assertStatus(201);
    $response->assertJsonPath('data.date', '2026-02-03');

    assertDatabaseHas('team_member_daily_availabilities', [
        'team_member_id' => $member->id,
        'date' => '2026-02-03 00:00:00',
        'availability' => 0.5,
    ]);
});

test('4.1.17 POST /api/holidays creates holiday', function () {
    $token = loginAsManager($this);

    $response = $this->postJson('/api/holidays', [
        'date' => '2026-02-20',
        'name' => 'Test Holiday',
        'description' => 'Test description',
    ], [
        'Authorization' => "Bearer {$token}",
    ]);

    $response->assertStatus(201);
    assertDatabaseHas('holidays', ['date' => '2026-02-20 00:00:00', 'name' => 'Test Holiday']);
});

test('4.1.17b POST /api/holidays returns 422 for duplicate date', function () {
    $token = loginAsManager($this);

    // Create first holiday
    $this->postJson('/api/holidays', [
        'date' => '2026-02-20',
        'name' => 'First Holiday',
    ], [
        'Authorization' => "Bearer {$token}",
    ])->assertStatus(201);

    // Try to create duplicate
    $response = $this->postJson('/api/holidays', [
        'date' => '2026-02-20',
        'name' => 'Duplicate Holiday',
    ], [
        'Authorization' => "Bearer {$token}",
    ]);

    $response->assertStatus(422);
    $response->assertJson([
        'message' => 'A holiday already exists for this date.',
        'errors' => [
            'date' => ['A holiday already exists for this date.'],
        ],
    ]);
});

test('4.1.18 POST /api/ptos creates PTO request', function () {
    $token = loginAsManager($this);
    $role = Role::factory()->create();
    $member = TeamMember::factory()->create(['role_id' => $role->id]);

    $response = $this->postJson('/api/ptos', [
        'team_member_id' => $member->id,
        'start_date' => '2026-02-10',
        'end_date' => '2026-02-11',
        'reason' => 'Refresh',
    ], [
        'Authorization' => "Bearer {$token}",
    ]);

    $response->assertStatus(201);
    $response->assertJsonPath('data.status', 'approved');
    assertDatabaseHas('ptos', ['team_member_id' => $member->id, 'status' => 'approved']);
});

test('4.1.19 PTO creation invalidates team and revenue caches', function () {
    $token = loginAsManager($this);
    $role = Role::factory()->create();
    $member = TeamMember::factory()->create([
        'role_id' => $role->id,
        'hourly_rate' => 80,
        'active' => true,
    ]);

    $month = '2026-02';

    $this->getJson("/api/capacity/team?month={$month}", [
        'Authorization' => "Bearer {$token}",
    ])->assertStatus(200);

    $this->getJson("/api/capacity/revenue?month={$month}", [
        'Authorization' => "Bearer {$token}",
    ])->assertStatus(200);

    $this->postJson('/api/ptos', [
        'team_member_id' => $member->id,
        'start_date' => '2026-02-26',
        'end_date' => '2026-02-28',
        'reason' => 'Vacation',
    ], [
        'Authorization' => "Bearer {$token}",
    ])->assertStatus(201);

    $teamResponse = $this->getJson("/api/capacity/team?month={$month}", [
        'Authorization' => "Bearer {$token}",
    ]);

    $teamResponse->assertStatus(200);
    expect((float) $teamResponse->json('data.person_days'))->toBe(18.0);
    expect($teamResponse->json('data.hours'))->toBe(144);

    $revenueResponse = $this->getJson("/api/capacity/revenue?month={$month}", [
        'Authorization' => "Bearer {$token}",
    ]);

    $revenueResponse->assertStatus(200);
    expect((float) $revenueResponse->json('data.possible_revenue'))->toBe(11520.0);
});

test('4.1.20 DELETE /api/ptos/{id} removes PTO and refreshes capacity', function () {
    $token = loginAsManager($this);
    $role = Role::factory()->create();
    $member = TeamMember::factory()->create([
        'role_id' => $role->id,
        'hourly_rate' => 80,
        'active' => true,
    ]);

    $createResponse = $this->postJson('/api/ptos', [
        'team_member_id' => $member->id,
        'start_date' => '2026-02-26',
        'end_date' => '2026-02-28',
        'reason' => 'Vacation',
    ], [
        'Authorization' => "Bearer {$token}",
    ]);

    $createResponse->assertStatus(201);
    $ptoId = $createResponse->json('data.id');

    $beforeDelete = $this->getJson("/api/capacity?month=2026-02&team_member_id={$member->id}", [
        'Authorization' => "Bearer {$token}",
    ]);
    $beforeDelete->assertStatus(200);
    expect((float) $beforeDelete->json('data.person_days'))->toBe(18.0);

    $deleteResponse = $this->deleteJson("/api/ptos/{$ptoId}", [], [
        'Authorization' => "Bearer {$token}",
    ]);
    $deleteResponse->assertStatus(200);
    $deleteResponse->assertJson(['message' => 'PTO deleted']);

    $afterDelete = $this->getJson("/api/capacity?month=2026-02&team_member_id={$member->id}", [
        'Authorization' => "Bearer {$token}",
    ]);
    $afterDelete->assertStatus(200);
    expect((float) $afterDelete->json('data.person_days'))->toBe(20.0);

    $this->deleteJson('/api/ptos/non-existent', [], [
        'Authorization' => "Bearer {$token}",
    ])->assertStatus(404);
});

test('1.1 POST /api/capacity/availability/batch saves multiple updates and returns 200 with saved count', function () {
    $token = loginAsManager($this);
    $role = Role::factory()->create();
    $member1 = TeamMember::factory()->create(['role_id' => $role->id]);
    $member2 = TeamMember::factory()->create(['role_id' => $role->id]);

    $payload = [
        'month' => '2026-02',
        'updates' => [
            ['team_member_id' => $member1->id, 'date' => '2026-02-03', 'availability' => 0.5],
            ['team_member_id' => $member1->id, 'date' => '2026-02-04', 'availability' => 0],
            ['team_member_id' => $member2->id, 'date' => '2026-02-05', 'availability' => 1],
        ],
    ];

    $response = $this->postJson('/api/capacity/availability/batch', $payload, [
        'Authorization' => "Bearer {$token}",
    ]);

    $response->assertStatus(200);
    $response->assertJsonPath('data.saved', 3);
    $response->assertJsonPath('data.month', '2026-02');

    assertDatabaseHas('team_member_daily_availabilities', [
        'team_member_id' => $member1->id,
        'date' => '2026-02-03 00:00:00',
        'availability' => 0.5,
    ]);

    assertDatabaseHas('team_member_daily_availabilities', [
        'team_member_id' => $member1->id,
        'date' => '2026-02-04 00:00:00',
        'availability' => 0,
    ]);

    assertDatabaseHas('team_member_daily_availabilities', [
        'team_member_id' => $member2->id,
        'date' => '2026-02-05 00:00:00',
        'availability' => 1,
    ]);
});

test('1.2 batch endpoint returns 422 when availability value is not in [0, 0.5, 1]', function () {
    $token = loginAsManager($this);
    $role = Role::factory()->create();
    $member = TeamMember::factory()->create(['role_id' => $role->id]);

    $payload = [
        'month' => '2026-02',
        'updates' => [
            ['team_member_id' => $member->id, 'date' => '2026-02-03', 'availability' => 0.75],
        ],
    ];

    $response = $this->postJson('/api/capacity/availability/batch', $payload, [
        'Authorization' => "Bearer {$token}",
    ]);

    $response->assertStatus(422);
    $response->assertJsonValidationErrors(['updates.0.availability']);
});

test('1.3 batch endpoint returns 422 when team_member_id does not exist', function () {
    $token = loginAsManager($this);

    $payload = [
        'month' => '2026-02',
        'updates' => [
            ['team_member_id' => 'non-existent-uuid', 'date' => '2026-02-03', 'availability' => 1],
        ],
    ];

    $response = $this->postJson('/api/capacity/availability/batch', $payload, [
        'Authorization' => "Bearer {$token}",
    ]);

    $response->assertStatus(422);
    $response->assertJsonValidationErrors(['updates.0.team_member_id']);
});

test('1.4 empty updates array returns 200 with saved count 0', function () {
    $token = loginAsManager($this);

    $payload = [
        'month' => '2026-02',
        'updates' => [],
    ];

    $response = $this->postJson('/api/capacity/availability/batch', $payload, [
        'Authorization' => "Bearer {$token}",
    ]);

    $response->assertStatus(200);
    $response->assertJsonPath('data.saved', 0);
    $response->assertJsonPath('data.month', '2026-02');
});

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

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

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