<?php

namespace Tests\Unit\Models;

use App\Enums\ApprovalStatusEnum;
use App\Events\RouteArticleApproved;
use App\Models\Article;
use App\Models\Feed;
use App\Models\PlatformChannel;
use App\Models\Route;
use App\Models\RouteArticle;
use Illuminate\Database\QueryException;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Event;
use Tests\TestCase;

class RouteArticleTest extends TestCase
{
    use RefreshDatabase;

    public function test_route_article_belongs_to_article(): void
    {
        /** @var RouteArticle $routeArticle */
        $routeArticle = RouteArticle::factory()->create();

        $this->assertInstanceOf(Article::class, $routeArticle->article);
    }

    public function test_route_article_belongs_to_feed(): void
    {
        /** @var RouteArticle $routeArticle */
        $routeArticle = RouteArticle::factory()->create();

        $this->assertInstanceOf(Feed::class, $routeArticle->feed);
    }

    public function test_route_article_belongs_to_platform_channel(): void
    {
        /** @var RouteArticle $routeArticle */
        $routeArticle = RouteArticle::factory()->create();

        $this->assertInstanceOf(PlatformChannel::class, $routeArticle->platformChannel);
    }

    public function test_route_article_has_default_pending_status(): void
    {
        /** @var RouteArticle $routeArticle */
        $routeArticle = RouteArticle::factory()->create();

        $this->assertEquals(ApprovalStatusEnum::PENDING, $routeArticle->approval_status);
        $this->assertTrue($routeArticle->isPending());
        $this->assertFalse($routeArticle->isApproved());
        $this->assertFalse($routeArticle->isRejected());
    }

    public function test_route_article_can_be_approved(): void
    {
        /** @var RouteArticle $routeArticle */
        $routeArticle = RouteArticle::factory()->create();

        $routeArticle->approve();
        $this->assertEquals(ApprovalStatusEnum::APPROVED, $routeArticle->fresh()->approval_status);
    }

    public function test_approving_dispatches_the_approved_event(): void
    {
        Event::fake([RouteArticleApproved::class]);

        /** @var RouteArticle $routeArticle */
        $routeArticle = RouteArticle::factory()->create();

        $routeArticle->approve();

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

    public function test_re_approving_an_approved_article_does_not_dispatch_again(): void
    {
        Event::fake([RouteArticleApproved::class]);

        /** @var RouteArticle $routeArticle */
        $routeArticle = RouteArticle::factory()->create();

        // A double-click, or a UI action racing an API call, calls approve() twice.
        // The second must be a no-op: each dispatch queues a publish listener.
        $routeArticle->approve();
        $routeArticle->approve();

        Event::assertDispatchedTimes(RouteArticleApproved::class, 1);
        $this->assertEquals(ApprovalStatusEnum::APPROVED, $routeArticle->fresh()->approval_status);
    }

    public function test_route_article_can_be_rejected(): void
    {
        /** @var RouteArticle $routeArticle */
        $routeArticle = RouteArticle::factory()->create();

        $routeArticle->reject();

        $this->assertEquals(ApprovalStatusEnum::REJECTED, $routeArticle->fresh()->approval_status);
    }

    public function test_article_has_many_route_articles(): void
    {
        /** @var Route $route1 */
        $route1 = Route::factory()->active()->create();
        /** @var Route $route2 */
        $route2 = Route::factory()->active()->create();
        $article = Article::factory()->create(['feed_id' => $route1->feed_id]);

        RouteArticle::factory()->forRoute($route1)->create(['article_id' => $article->id]);
        RouteArticle::factory()->forRoute($route2)->create(['article_id' => $article->id]);

        $this->assertCount(2, $article->routeArticles);
    }

    public function test_route_has_many_route_articles(): void
    {
        /** @var Route $route */
        $route = Route::factory()->active()->create();
        $article1 = Article::factory()->create(['feed_id' => $route->feed_id]);
        $article2 = Article::factory()->create(['feed_id' => $route->feed_id]);

        RouteArticle::factory()->forRoute($route)->create(['article_id' => $article1->id]);
        RouteArticle::factory()->forRoute($route)->create(['article_id' => $article2->id]);

        $this->assertCount(2, $route->routeArticles);
    }

    public function test_unique_constraint_prevents_duplicate_route_articles(): void
    {
        /** @var Route $route */
        $route = Route::factory()->active()->create();
        $article = Article::factory()->create(['feed_id' => $route->feed_id]);

        RouteArticle::factory()->forRoute($route)->create(['article_id' => $article->id]);

        $this->expectException(QueryException::class);

        RouteArticle::factory()->forRoute($route)->create(['article_id' => $article->id]);
    }

    public function test_route_article_cascade_deletes_when_article_deleted(): void
    {
        /** @var RouteArticle $routeArticle */
        $routeArticle = RouteArticle::factory()->create();
        $articleId = $routeArticle->article_id;

        Article::destroy($articleId);

        $this->assertDatabaseMissing('route_articles', ['article_id' => $articleId]);
    }

    public function test_route_article_cascade_deletes_when_route_deleted(): void
    {
        /** @var Route $route */
        $route = Route::factory()->active()->create();
        $article = Article::factory()->create(['feed_id' => $route->feed_id]);
        RouteArticle::factory()->forRoute($route)->create(['article_id' => $article->id]);

        Route::where('feed_id', $route->feed_id)
            ->where('platform_channel_id', $route->platform_channel_id)
            ->delete();

        $this->assertDatabaseMissing('route_articles', [
            'feed_id' => $route->feed_id,
            'platform_channel_id' => $route->platform_channel_id,
        ]);
    }

    public function test_route_article_belongs_to_route(): void
    {
        /** @var Route $route */
        $route = Route::factory()->active()->create();
        $article = Article::factory()->create(['feed_id' => $route->feed_id]);
        /** @var RouteArticle $routeArticle */
        $routeArticle = RouteArticle::factory()->forRoute($route)->create(['article_id' => $article->id]);

        $loadedRoute = $routeArticle->route;

        $this->assertInstanceOf(Route::class, $loadedRoute);
        $this->assertEquals($route->feed_id, $loadedRoute->feed_id);
        $this->assertEquals($route->platform_channel_id, $loadedRoute->platform_channel_id);
    }
}
