<?php

namespace Tests\Tenant;

use Carbon\Carbon;
use Illuminate\Support\Str;
use Mtc\MercuryDataModels\Booking;
use Mtc\MercuryDataModels\BookingAvailability;
use Mtc\MercuryDataModels\BookingStatus;
use Mtc\MercuryDataModels\Dealership;
use Tests\TenantTestCase;
use Tests\UserForTenant;

class ServiceCalendarTest extends TenantTestCase
{
    use UserForTenant;

    public function testCalendarWeekViewReturnsSevenDays()
    {
        $monday = Carbon::now()->startOfWeek();

        $response = $this->actingAs($this->getUser())
            ->postJson('/api/service-bookings/calendar', [
                'period' => 'week',
                'from' => $monday->format('Y-m-d'),
            ]);

        $response->assertStatus(200);

        $data = $response->json('data');
        $firstWeek = collect($data)->first();
        $this->assertCount(7, $firstWeek);

        // Verify each day has the expected structure
        foreach ($firstWeek as $day) {
            $this->assertArrayHasKey('name', $day);
            $this->assertArrayHasKey('weekday', $day);
            $this->assertArrayHasKey('week_of_year', $day);
            $this->assertArrayHasKey('full-date', $day);
            $this->assertArrayHasKey('entries', $day);
        }
    }

    public function testCalendarMonthViewReturnsMultipleWeeks()
    {
        $response = $this->actingAs($this->getUser())
            ->postJson('/api/service-bookings/calendar', [
                'period' => 'month',
                'from' => Carbon::now()->startOfMonth()->format('Y-m-d'),
            ]);

        $response->assertStatus(200);
        $data = $response->json('data');
        // A month view should have 4-6 weeks
        $this->assertGreaterThanOrEqual(4, count($data));
        $this->assertLessThanOrEqual(6, count($data));

        // Each week should have 7 days
        foreach ($data as $week) {
            $this->assertCount(7, $week);
        }
    }

    public function testCalendarExcludesDraftAndCancelledBookings()
    {
        $dealership = Dealership::factory()->create();
        $bookingDate = Carbon::now()->startOfWeek()->addDay(); // Tuesday of current week

        // Visible bookings
        Booking::factory()->create([
            'uuid' => Str::random(16),
            'status' => BookingStatus::PENDING->value,
            'booking_time' => $bookingDate->copy()->setTime(9, 0),
            'location_id' => $dealership->id,
            'first_name' => 'Visible',
        ]);
        Booking::factory()->create([
            'uuid' => Str::random(16),
            'status' => BookingStatus::CONFIRMED->value,
            'booking_time' => $bookingDate->copy()->setTime(11, 0),
            'location_id' => $dealership->id,
            'first_name' => 'AlsoVisible',
        ]);

        // Hidden bookings
        Booking::factory()->create([
            'uuid' => Str::random(16),
            'status' => BookingStatus::DRAFT->value,
            'booking_time' => $bookingDate->copy()->setTime(13, 0),
            'location_id' => $dealership->id,
            'first_name' => 'DraftHidden',
        ]);
        Booking::factory()->create([
            'uuid' => Str::random(16),
            'status' => BookingStatus::CANCELLED->value,
            'booking_time' => $bookingDate->copy()->setTime(15, 0),
            'location_id' => $dealership->id,
            'first_name' => 'CancelledHidden',
        ]);

        $response = $this->actingAs($this->getUser())
            ->postJson('/api/service-bookings/calendar', [
                'period' => 'week',
                'from' => Carbon::now()->startOfWeek()->format('Y-m-d'),
            ]);

        $response->assertStatus(200);

        // Find the day with bookings (Tuesday)
        $data = $response->json('data');
        $firstWeek = collect($data)->first();
        $targetDate = $bookingDate->format('Y-m-d');
        $day = collect($firstWeek)->firstWhere('full-date', $targetDate);

        $this->assertNotNull($day, "Expected day $targetDate in calendar");

        // Count total bookings across all location entries
        $totalBookings = collect($day['entries'])->sum(fn ($loc) => count($loc['entries']));
        $this->assertEquals(2, $totalBookings);
    }

    public function testCalendarFiltersByLocation()
    {
        $dealerA = Dealership::factory()->create(['name' => 'Dealer A']);
        $dealerB = Dealership::factory()->create(['name' => 'Dealer B']);
        $bookingDate = Carbon::now()->startOfWeek()->addDay();

        Booking::factory()->create([
            'uuid' => Str::random(16),
            'status' => BookingStatus::PENDING->value,
            'booking_time' => $bookingDate->copy()->setTime(9, 0),
            'location_id' => $dealerA->id,
        ]);
        Booking::factory()->create([
            'uuid' => Str::random(16),
            'status' => BookingStatus::PENDING->value,
            'booking_time' => $bookingDate->copy()->setTime(11, 0),
            'location_id' => $dealerB->id,
        ]);

        // Filter to only dealer A
        $response = $this->actingAs($this->getUser())
            ->postJson('/api/service-bookings/calendar', [
                'period' => 'week',
                'from' => Carbon::now()->startOfWeek()->format('Y-m-d'),
                'location' => $dealerA->id,
            ]);

        $response->assertStatus(200);
        $data = $response->json('data');
        $firstWeek = collect($data)->first();
        $targetDate = $bookingDate->format('Y-m-d');
        $day = collect($firstWeek)->firstWhere('full-date', $targetDate);

        // Only dealer A's booking should appear
        $totalBookings = collect($day['entries'])->sum(fn ($loc) => count($loc['entries']));
        $this->assertEquals(1, $totalBookings);
    }

    public function testCalendarSearchByName()
    {
        $dealership = Dealership::factory()->create();
        $bookingDate = Carbon::now()->startOfWeek()->addDay();

        Booking::factory()->create([
            'uuid' => Str::random(16),
            'status' => BookingStatus::PENDING->value,
            'booking_time' => $bookingDate->copy()->setTime(9, 0),
            'location_id' => $dealership->id,
            'first_name' => 'SpecificSearchName',
        ]);
        Booking::factory()->create([
            'uuid' => Str::random(16),
            'status' => BookingStatus::PENDING->value,
            'booking_time' => $bookingDate->copy()->setTime(11, 0),
            'location_id' => $dealership->id,
            'first_name' => 'OtherPerson',
        ]);

        $response = $this->actingAs($this->getUser())
            ->postJson('/api/service-bookings/calendar', [
                'period' => 'week',
                'from' => Carbon::now()->startOfWeek()->format('Y-m-d'),
                'search' => 'SpecificSearchName',
            ]);

        $response->assertStatus(200);
        $data = $response->json('data');
        $firstWeek = collect($data)->first();
        $targetDate = $bookingDate->format('Y-m-d');
        $day = collect($firstWeek)->firstWhere('full-date', $targetDate);

        $totalBookings = collect($day['entries'])->sum(fn ($loc) => count($loc['entries']));
        $this->assertEquals(1, $totalBookings);
    }

    public function testCalendarGroupsBookingsByLocation()
    {
        $dealerA = Dealership::factory()->create(['name' => 'Dealer A']);
        $dealerB = Dealership::factory()->create(['name' => 'Dealer B']);
        $bookingDate = Carbon::now()->startOfWeek()->addDay();

        Booking::factory()->create([
            'uuid' => Str::random(16),
            'status' => BookingStatus::PENDING->value,
            'booking_time' => $bookingDate->copy()->setTime(9, 0),
            'location_id' => $dealerA->id,
        ]);
        Booking::factory()->create([
            'uuid' => Str::random(16),
            'status' => BookingStatus::PENDING->value,
            'booking_time' => $bookingDate->copy()->setTime(10, 0),
            'location_id' => $dealerA->id,
        ]);
        Booking::factory()->create([
            'uuid' => Str::random(16),
            'status' => BookingStatus::PENDING->value,
            'booking_time' => $bookingDate->copy()->setTime(11, 0),
            'location_id' => $dealerB->id,
        ]);

        $response = $this->actingAs($this->getUser())
            ->postJson('/api/service-bookings/calendar', [
                'period' => 'week',
                'from' => Carbon::now()->startOfWeek()->format('Y-m-d'),
            ]);

        $response->assertStatus(200);
        $data = $response->json('data');
        $firstWeek = collect($data)->first();
        $targetDate = $bookingDate->format('Y-m-d');
        $day = collect($firstWeek)->firstWhere('full-date', $targetDate);

        // Should have 2 location groups
        $this->assertCount(2, $day['entries']);

        $locationNames = collect($day['entries'])->pluck('name')->toArray();
        $this->assertContains('Dealer A', $locationNames);
        $this->assertContains('Dealer B', $locationNames);

        // Dealer A should have 2 bookings, Dealer B should have 1
        $dealerAEntry = collect($day['entries'])->firstWhere('name', 'Dealer A');
        $dealerBEntry = collect($day['entries'])->firstWhere('name', 'Dealer B');
        $this->assertCount(2, $dealerAEntry['entries']);
        $this->assertCount(1, $dealerBEntry['entries']);
    }

    public function testCalendarIncludesLocationsAndViewFormats()
    {
        $dealership = Dealership::factory()->create(['name' => 'Active Dealer']);
        BookingAvailability::create([
            'dealership_id' => $dealership->id,
            'active' => true,
            'number_of_bays' => 3,
        ]);

        $response = $this->actingAs($this->getUser())
            ->postJson('/api/service-bookings/calendar', [
                'period' => 'week',
                'from' => Carbon::now()->startOfWeek()->format('Y-m-d'),
            ]);

        $response->assertStatus(200);
        $this->assertArrayHasKey('locations', $response->json());
        $this->assertArrayHasKey('view_formats', $response->json());
        $this->assertArrayHasKey('period', $response->json());

        // View formats should include week and month
        $this->assertEquals(['week' => 'Week', 'month' => 'Month'], $response->json('view_formats'));

        // Locations should start with 'All' followed by active locations
        $locations = $response->json('locations');
        $this->assertEquals('all', $locations[0]['id']);
        $this->assertEquals('All', $locations[0]['name']);
        $this->assertEquals('Active Dealer', $locations[1]['name']);
    }

    public function testCalendarWeekPeriodFormattedCorrectly()
    {
        $monday = Carbon::parse('2026-03-02'); // A Monday

        $response = $this->actingAs($this->getUser())
            ->postJson('/api/service-bookings/calendar', [
                'period' => 'week',
                'from' => $monday->format('Y-m-d'),
            ]);

        $response->assertStatus(200);
        $period = $response->json('period');
        // Week format: "02 Mar - 08 Mar"
        $this->assertStringContainsString('02 Mar', $period);
        $this->assertStringContainsString('08 Mar', $period);
    }

    public function testCalendarMonthPeriodFormattedCorrectly()
    {
        $response = $this->actingAs($this->getUser())
            ->postJson('/api/service-bookings/calendar', [
                'period' => 'month',
                'from' => '2026-03-15',
            ]);

        $response->assertStatus(200);
        $period = $response->json('period');
        // Month format: "March 2026"
        $this->assertEquals('March 2026', $period);
    }

    public function testCalendarAllLocationShowsAllBookings()
    {
        $dealerA = Dealership::factory()->create();
        $dealerB = Dealership::factory()->create();
        $bookingDate = Carbon::now()->startOfWeek()->addDay();

        Booking::factory()->create([
            'uuid' => Str::random(16),
            'status' => BookingStatus::PENDING->value,
            'booking_time' => $bookingDate->copy()->setTime(9, 0),
            'location_id' => $dealerA->id,
        ]);
        Booking::factory()->create([
            'uuid' => Str::random(16),
            'status' => BookingStatus::PENDING->value,
            'booking_time' => $bookingDate->copy()->setTime(11, 0),
            'location_id' => $dealerB->id,
        ]);

        // Pass location='all' to see all locations
        $response = $this->actingAs($this->getUser())
            ->postJson('/api/service-bookings/calendar', [
                'period' => 'week',
                'from' => Carbon::now()->startOfWeek()->format('Y-m-d'),
                'location' => 'all',
            ]);

        $response->assertStatus(200);
        $data = $response->json('data');
        $firstWeek = collect($data)->first();
        $targetDate = $bookingDate->format('Y-m-d');
        $day = collect($firstWeek)->firstWhere('full-date', $targetDate);

        $totalBookings = collect($day['entries'])->sum(fn ($loc) => count($loc['entries']));
        $this->assertEquals(2, $totalBookings);
    }
}
