<?php

namespace Tests\Feature;

use App\Actions\CreateRouteArticlesAction;
use App\Actions\FetchArticleDataAction;
use App\Actions\ValidateArticleAction;
use App\Enums\ApprovalStatusEnum;
use App\Livewire\Articles;
use App\Models\Article;
use App\Models\Feed;
use App\Models\Keyword;
use App\Models\PlatformChannel;
use App\Models\Route;
use App\Models\RouteArticle;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Mockery;
use Tests\TestCase;

class RouteArticleDecisionStampingTest extends TestCase
{
    use RefreshDatabase;

    public function test_clearing_pending_articles_stamps_every_rejected_row(): void
    {
        $pending = RouteArticle::factory()->count(3)->create([
            'approval_status' => ApprovalStatusEnum::PENDING,
            'decided_at' => null,
        ]);

        Livewire::test(Articles::class)->call('clear');

        foreach ($pending as $routeArticle) {
            /** @var RouteArticle $fresh */
            $fresh = $routeArticle->fresh();

            $this->assertNotNull($fresh->decided_at);
        }
    }

    public function test_the_api_clear_endpoint_stamps_every_rejected_row(): void
    {
        $pending = RouteArticle::factory()->count(3)->create([
            'approval_status' => ApprovalStatusEnum::PENDING,
            'decided_at' => null,
        ]);

        $this->actingAs(User::factory()->create())
            ->postJson('/api/v1/route-articles/clear')
            ->assertOk();

        foreach ($pending as $routeArticle) {
            /** @var RouteArticle $fresh */
            $fresh = $routeArticle->fresh();

            $this->assertNotNull($fresh->decided_at);
        }
    }

    public function test_restoring_to_pending_clears_the_decision_time(): void
    {
        /** @var RouteArticle $routeArticle */
        $routeArticle = RouteArticle::factory()->create([
            'approval_status' => ApprovalStatusEnum::REJECTED,
            'decided_at' => now(),
        ]);

        Livewire::test(Articles::class)->call('restore', $routeArticle->id);

        $this->assertNull($routeArticle->fresh()->decided_at);
    }

    public function test_the_api_restore_endpoint_clears_the_decision_time(): void
    {
        /** @var RouteArticle $routeArticle */
        $routeArticle = RouteArticle::factory()->create([
            'approval_status' => ApprovalStatusEnum::REJECTED,
            'decided_at' => now(),
        ]);

        $this->actingAs(User::factory()->create())
            ->postJson("/api/v1/route-articles/{$routeArticle->id}/restore")
            ->assertOk();

        $this->assertNull($routeArticle->fresh()->decided_at);
    }

    public function test_auto_approved_articles_are_stamped_on_creation(): void
    {
        $article = $this->articleOnRouteWithoutKeywords(autoApprove: true);

        $this->validate($article);

        $routeArticle = RouteArticle::where('article_id', $article->id)->firstOrFail();

        $this->assertSame(ApprovalStatusEnum::APPROVED, $routeArticle->approval_status);
        $this->assertNotNull($routeArticle->decided_at);
    }

    public function test_keyword_rejected_articles_are_stamped_on_creation(): void
    {
        $article = $this->articleOnRouteWithKeyword('nothing-that-matches');

        $this->validate($article);

        $routeArticle = RouteArticle::where('article_id', $article->id)->firstOrFail();

        $this->assertSame(ApprovalStatusEnum::REJECTED, $routeArticle->approval_status);
        $this->assertNotNull($routeArticle->decided_at);
    }

    public function test_pending_articles_are_not_stamped_on_creation(): void
    {
        $article = $this->articleOnRouteWithoutKeywords(autoApprove: false);

        $this->validate($article);

        $routeArticle = RouteArticle::where('article_id', $article->id)->firstOrFail();

        $this->assertSame(ApprovalStatusEnum::PENDING, $routeArticle->approval_status);
        $this->assertNull($routeArticle->decided_at);
    }

    private function validate(Article $article): void
    {
        $fetcher = Mockery::mock(FetchArticleDataAction::class);
        $fetcher->shouldReceive('execute')
            ->with($article)
            ->once()
            ->andReturn([
                'title' => 'Test Title',
                'description' => 'Test description',
                'full_article' => 'Body text',
            ]);

        (new ValidateArticleAction($fetcher, new CreateRouteArticlesAction))->execute($article);
    }

    private function articleOnRouteWithoutKeywords(bool $autoApprove): Article
    {
        $feed = Feed::factory()->create();
        $channel = PlatformChannel::factory()->create();

        Route::factory()->create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $channel->id,
            'is_active' => true,
            'auto_approve' => $autoApprove,
        ]);

        return Article::factory()->create(['feed_id' => $feed->id, 'validated_at' => null]);
    }

    private function articleOnRouteWithKeyword(string $keyword): Article
    {
        $feed = Feed::factory()->create();
        $channel = PlatformChannel::factory()->create();

        Route::factory()->create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $channel->id,
            'is_active' => true,
        ]);

        Keyword::factory()->active()->create([
            'feed_id' => $feed->id,
            'platform_channel_id' => $channel->id,
            'keyword' => $keyword,
        ]);

        return Article::factory()->create(['feed_id' => $feed->id, 'validated_at' => null]);
    }
}
