<?php

namespace Tests\Tenant\Services;

use App\Services\GraphService;
use Carbon\Carbon;
use Illuminate\Support\Facades\Http;
use Mtc\MercuryDataModels\ResourceView;
use Tests\TenantTestCase;

class GraphServiceTest extends TenantTestCase
{
    public function testReturnsGraphResponseShape(): void
    {
        Http::fake();

        $response = app(GraphService::class)->getGraph([
            'metrics' => 'vehicles',
            'date_range' => 7,
        ]);

        $this->assertSame('ok', $response['status']);
        $this->assertSame('vehicles', $response['type']);
        $this->assertSame(7, $response['date_range']);
        $this->assertArrayHasKey('labels', $response);
        $this->assertArrayHasKey('datasets', $response);
    }

    public function testUnknownMetricReturnsEmptyDatasets(): void
    {
        Http::fake();

        $response = app(GraphService::class)->getGraph([
            'metrics' => 'no-such-metric',
            'date_range' => 7,
        ]);

        $this->assertSame('ok', $response['status']);
        $this->assertSame([], $response['datasets']);
    }

    public function testCustomFromToRangeIsHonoured(): void
    {
        Http::fake();

        $response = app(GraphService::class)->getGraph([
            'metrics' => 'vehicles',
            'from_date' => '2024-01-01',
            'to_date' => '2024-01-05',
        ]);

        $this->assertSame('ok', $response['status']);
        $this->assertSame('2024-01-01', $response['from']);
        $this->assertSame('2024-01-05', $response['to']);
        $this->assertSame(4, $response['date_range']);
        // Inclusive day range: 1st through 5th == 5 labels.
        $this->assertCount(5, $response['labels']);
    }

    public function testDateRangeFallbackUsedWhenNoCustomRange(): void
    {
        Http::fake();

        $response = app(GraphService::class)->getGraph([
            'metrics' => 'vehicles',
            'date_range' => 14,
        ]);

        $this->assertSame('ok', $response['status']);
        $this->assertSame(14, $response['date_range']);
    }

    public function testSingleMetricGraphsReturnOneDataset(): void
    {
        Http::fake();

        $metrics = ['enquiries', 'vehicles', 'vehicles-added', 'vehicles-removed', 'vehicles-reserved', 'vehicles-views'];
        foreach ($metrics as $metric) {
            $response = app(GraphService::class)->getGraph([
                'metrics' => $metric,
                'date_range' => 7,
            ]);

            $this->assertSame('ok', $response['status'], $metric);
            $this->assertSame($metric, $response['type']);
            $this->assertCount(1, $response['datasets'], "$metric should have a single series");
        }
    }

    public function testVehicleViewsSumsResourceViewHitsByDateAndDropsToday(): void
    {
        Http::fake();

        $yesterday = Carbon::yesterday();

        // Two vehicles viewed on the same (complete) day should be summed into one daily value.
        ResourceView::create([
            'viewable_type' => 'vehicle',
            'viewable_id' => 1,
            'date' => $yesterday,
            'hits' => 10,
        ]);
        ResourceView::create([
            'viewable_type' => 'vehicle',
            'viewable_id' => 2,
            'date' => $yesterday,
            'hits' => 5,
        ]);
        // Non-vehicle views must be ignored.
        ResourceView::create([
            'viewable_type' => 'offer',
            'viewable_id' => 1,
            'date' => $yesterday,
            'hits' => 100,
        ]);
        // Today's stat is incomplete (written at the start of the next day) and must be dropped.
        ResourceView::create([
            'viewable_type' => 'vehicle',
            'viewable_id' => 1,
            'date' => Carbon::today(),
            'hits' => 999,
        ]);

        $response = app(GraphService::class)->getGraph([
            'metrics' => 'vehicles-views',
            'date_range' => 7,
        ]);

        $this->assertSame('ok', $response['status']);
        $this->assertCount(1, $response['datasets']);

        // Today is dropped, so the series ends on yesterday with the summed vehicle hits (10 + 5),
        // and today's 999 never appears.
        $data = $response['datasets'][0]['data']->values()->toArray();
        $this->assertSame(15, end($data), 'Last point should be yesterday totalling 15 hits (10 + 5)');
        $this->assertNotContains(999, $data, "Today's incomplete stat must be excluded");
        $this->assertNotContains(100, $data, 'Non-vehicle views must be excluded');
    }

    public function testTodayIsDroppedFromAllMetrics(): void
    {
        Http::fake();

        $today = Carbon::today()->format('jS M');

        // Today's data is always partial (counts still accumulating, end-of-day
        // report not yet run), so every trend graph drops today.
        $vehicles = app(GraphService::class)->getGraph(['metrics' => 'vehicles', 'date_range' => 7]);
        $enquiries = app(GraphService::class)->getGraph(['metrics' => 'enquiries', 'date_range' => 7]);

        $this->assertNotContains(
            $today,
            $vehicles['labels']->toArray(),
            'Total vehicles should drop today (end-of-day report not yet run)',
        );
        $this->assertNotContains(
            $today,
            $enquiries['labels']->toArray(),
            'Enquiries should drop today (day not finished)',
        );
        $this->assertCount(
            $enquiries['labels']->count(),
            $vehicles['labels'],
            'Every metric drops today, so their series span the same number of days',
        );
    }

    public function testTotalVehiclesDropsTodayOnCustomRangeEndingToday(): void
    {
        Http::fake();

        // The dashboard sends every range (including the 30/60/90d presets) as
        // from/to dates, so today must be dropped regardless of how it's expressed.
        $response = app(GraphService::class)->getGraph([
            'metrics' => 'vehicles',
            'from_date' => Carbon::today()->subDays(5)->format('Y-m-d'),
            'to_date' => Carbon::today()->format('Y-m-d'),
        ]);

        $this->assertNotContains(
            Carbon::today()->format('jS M'),
            $response['labels']->toArray(),
            'Total vehicles is always empty today, so today is dropped for from/to ranges too',
        );
    }

    public function testVehiclesAddedDropsTodayOnCustomRangeEndingToday(): void
    {
        Http::fake();

        // The dashboard sends the 30/60/90d presets as from/to dates, so today
        // must be dropped for the sub-metrics too, however the range is expressed.
        $response = app(GraphService::class)->getGraph([
            'metrics' => 'vehicles-added',
            'from_date' => Carbon::today()->subDays(5)->format('Y-m-d'),
            'to_date' => Carbon::today()->format('Y-m-d'),
        ]);

        $this->assertNotContains(
            Carbon::today()->format('jS M'),
            $response['labels']->toArray(),
            'Vehicle sub-metrics drop today (day not finished)',
        );
    }
}
