<?php

namespace Tests\Feature;

use App\Actions\BackfillRouteArticlesAction;
use App\Actions\CreateRouteAction;
use App\Actions\CreateRouteArticlesAction;
use App\Events\RouteActivated;
use App\Listeners\BackfillRouteArticlesListener;
use App\Livewire\Routes;
use App\Models\Article;
use App\Models\Feed;
use App\Models\PlatformChannel;
use App\Models\Route;
use App\Models\RouteArticle;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Event;
use Livewire\Livewire;
use Tests\TestCase;

class BackfillRouteArticlesTest extends TestCase
{
    use RefreshDatabase;

    private function listener(): BackfillRouteArticlesListener
    {
        return new BackfillRouteArticlesListener(new BackfillRouteArticlesAction(new CreateRouteArticlesAction));
    }

    private function strandedArticle(Route $route): Article
    {
        return Article::factory()->create([
            'feed_id' => $route->feed_id,
            'content' => 'Some article content',
        ]);
    }

    private function makeRoute(bool $isActive): Route
    {
        $feed = Feed::factory()->create();
        $channel = PlatformChannel::factory()->create();

        return Route::create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $channel->id,
            'priority' => 50,
            'is_active' => $isActive,
        ]);
    }

    public function test_listener_backfills_stranded_articles(): void
    {
        $route = $this->makeRoute(true);
        $article = $this->strandedArticle($route);

        $this->listener()->handle(new RouteActivated($route->feed_id, $route->platform_channel_id));

        $this->assertSame(1, RouteArticle::where('article_id', $article->id)->count());
    }

    public function test_listener_does_nothing_when_the_route_is_missing(): void
    {
        $this->listener()->handle(new RouteActivated(99999, 99999));

        $this->assertSame(0, RouteArticle::count());
    }

    public function test_listener_does_nothing_when_the_route_is_inactive(): void
    {
        $route = $this->makeRoute(false);
        $this->strandedArticle($route);

        $this->listener()->handle(new RouteActivated($route->feed_id, $route->platform_channel_id));

        $this->assertSame(0, RouteArticle::count());
    }

    public function test_creating_a_route_backfills_articles_that_were_validated_with_no_route(): void
    {
        $feed = Feed::factory()->create();
        $channel = PlatformChannel::factory()->create();
        $article = Article::factory()->create([
            'feed_id' => $feed->id,
            'content' => 'Some article content',
        ]);

        (new CreateRouteAction)->execute($feed->id, $channel->id);

        $this->assertSame(1, RouteArticle::where('article_id', $article->id)->count());
    }

    public function test_toggling_a_route_inactive_then_active_backfills(): void
    {
        $route = $this->makeRoute(false);
        $article = $this->strandedArticle($route);

        Livewire::test(Routes::class)
            ->call('toggle', $route->feed_id, $route->platform_channel_id);

        $this->assertTrue($route->fresh()->is_active);
        $this->assertSame(1, RouteArticle::where('article_id', $article->id)->count());
    }

    public function test_create_route_action_dispatches_route_activated(): void
    {
        Event::fake([RouteActivated::class]);

        $feed = Feed::factory()->create();
        $channel = PlatformChannel::factory()->create();

        (new CreateRouteAction)->execute($feed->id, $channel->id);

        Event::assertDispatched(
            RouteActivated::class,
            fn (RouteActivated $event) => $event->feedId === $feed->id && $event->platformChannelId === $channel->id
        );
    }

    public function test_create_route_action_does_not_dispatch_for_an_existing_route(): void
    {
        Event::fake([RouteActivated::class]);

        $feed = Feed::factory()->create();
        $channel = PlatformChannel::factory()->create();

        (new CreateRouteAction)->execute($feed->id, $channel->id);
        (new CreateRouteAction)->execute($feed->id, $channel->id);

        Event::assertDispatchedTimes(RouteActivated::class, 1);
    }

    public function test_create_route_action_does_not_dispatch_for_an_inactive_route(): void
    {
        Event::fake([RouteActivated::class]);

        $feed = Feed::factory()->create();
        $channel = PlatformChannel::factory()->create();

        (new CreateRouteAction)->execute($feed->id, $channel->id, 0, false);

        Event::assertNotDispatched(RouteActivated::class);
    }
}
