<?php

namespace Tests\Unit\Dashboard\Stats;

use App\Dashboard\Stats\PublishSuccessRate;
use App\Enums\PublishStatusEnum;
use App\Models\RouteArticle;
use App\Support\DateRange;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Carbon;
use Tests\TestCase;

class PublishSuccessRateTest extends TestCase
{
    use RefreshDatabase;

    private function range(): DateRange
    {
        return new DateRange(
            Carbon::parse('2026-07-01 00:00:00'),
            Carbon::parse('2026-07-31 23:59:59'),
        );
    }

    /**
     * @return array<string, float|null>
     */
    private function values(): array
    {
        $result = (new PublishSuccessRate)->for($this->range());

        return array_combine($result->labels, $result->series[0]->values);
    }

    private function attempt(PublishStatusEnum $status, string $updatedAt): RouteArticle
    {
        /** @var RouteArticle $article */
        $article = RouteArticle::factory()->create(['publish_status' => $status]);

        // Model::update() would re-stamp updated_at to now(); bypass via the query builder.
        RouteArticle::query()
            ->whereKey($article->getKey())
            ->update(['updated_at' => Carbon::parse($updatedAt)]);

        return $article;
    }

    public function test_it_computes_the_published_share_of_daily_attempts(): void
    {
        $this->attempt(PublishStatusEnum::PUBLISHED, '2026-07-10 09:00:00');
        $this->attempt(PublishStatusEnum::PUBLISHED, '2026-07-10 10:00:00');
        $this->attempt(PublishStatusEnum::PUBLISHED, '2026-07-10 11:00:00');
        $this->attempt(PublishStatusEnum::ERROR, '2026-07-10 12:00:00');

        $this->assertSame(75.0, $this->values()['2026-07-10']);
    }

    public function test_it_excludes_skipped_articles_from_the_denominator(): void
    {
        $this->attempt(PublishStatusEnum::PUBLISHED, '2026-07-10 09:00:00');
        $this->attempt(PublishStatusEnum::SKIPPED, '2026-07-10 10:00:00');

        $this->assertSame(100.0, $this->values()['2026-07-10']);
    }

    public function test_it_excludes_articles_still_in_flight(): void
    {
        $this->attempt(PublishStatusEnum::PUBLISHED, '2026-07-10 09:00:00');
        $this->attempt(PublishStatusEnum::PUBLISHING, '2026-07-10 10:00:00');
        $this->attempt(PublishStatusEnum::UNPUBLISHED, '2026-07-10 11:00:00');

        $this->assertSame(100.0, $this->values()['2026-07-10']);
    }

    public function test_it_reports_no_rate_for_a_day_with_no_attempts(): void
    {
        $this->attempt(PublishStatusEnum::PUBLISHED, '2026-07-10 09:00:00');

        $this->assertNull($this->values()['2026-07-11']);
    }

    public function test_it_reports_a_zero_rate_when_every_attempt_failed(): void
    {
        $this->attempt(PublishStatusEnum::ERROR, '2026-07-10 09:00:00');

        $this->assertSame(0.0, $this->values()['2026-07-10']);
    }

    public function test_it_counts_an_article_on_the_day_it_was_last_touched(): void
    {
        $this->attempt(PublishStatusEnum::ERROR, '2026-07-10 09:00:00');
        $this->attempt(PublishStatusEnum::PUBLISHED, '2026-07-12 09:00:00');

        $values = $this->values();

        $this->assertSame(0.0, $values['2026-07-10']);
        $this->assertSame(100.0, $values['2026-07-12']);
    }

    public function test_a_retry_moves_the_article_to_the_retry_day(): void
    {
        $article = $this->attempt(PublishStatusEnum::ERROR, '2026-07-10 09:00:00');

        RouteArticle::query()
            ->whereKey($article->getKey())
            ->update([
                'publish_status' => PublishStatusEnum::PUBLISHED,
                'updated_at' => Carbon::parse('2026-07-12 09:00:00'),
            ]);

        $values = $this->values();

        $this->assertNull($values['2026-07-10']);
        $this->assertSame(100.0, $values['2026-07-12']);
    }

    public function test_it_ignores_attempts_outside_the_range(): void
    {
        $this->attempt(PublishStatusEnum::PUBLISHED, '2026-06-30 23:00:00');
        $this->attempt(PublishStatusEnum::ERROR, '2026-08-01 01:00:00');

        $this->assertSame([], array_filter($this->values(), fn (?float $value): bool => $value !== null));
    }

    public function test_it_rounds_the_rate_to_one_decimal(): void
    {
        $this->attempt(PublishStatusEnum::PUBLISHED, '2026-07-10 09:00:00');
        $this->attempt(PublishStatusEnum::PUBLISHED, '2026-07-10 10:00:00');
        $this->attempt(PublishStatusEnum::ERROR, '2026-07-10 11:00:00');

        $this->assertSame(66.7, $this->values()['2026-07-10']);
    }

    public function test_it_refuses_to_bucket_a_range_wider_than_the_day_limit(): void
    {
        $result = (new PublishSuccessRate)->for(new DateRange(
            Carbon::parse('2020-01-01 00:00:00'),
            Carbon::parse('2026-12-31 23:59:59'),
        ));

        $this->assertTrue($result->isTooWide());
    }
}
